roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<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>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
366     {
367         return Roo.get(document.body);
368     },
369     
370     /**
371      * Fetch the element to display the tooltip on.
372      * @return {Roo.Element} defaults to this.el
373      */
374     tooltipEl : function()
375     {
376         return this.el;
377     },
378         
379     addxtype  : function(tree,cntr)
380     {
381         var cn = this;
382         
383         cn = Roo.factory(tree);
384         //Roo.log(['addxtype', cn]);
385            
386         cn.parentType = this.xtype; //??
387         cn.parentId = this.id;
388         
389         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390         if (typeof(cn.container_method) == 'string') {
391             cntr = cn.container_method;
392         }
393         
394         
395         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
396         
397         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
398         
399         var build_from_html =  Roo.XComponent.build_from_html;
400           
401         var is_body  = (tree.xtype == 'Body') ;
402           
403         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
404           
405         var self_cntr_el = Roo.get(this[cntr](false));
406         
407         // do not try and build conditional elements 
408         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
409             return false;
410         }
411         
412         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414                 return this.addxtypeChild(tree,cntr, is_body);
415             }
416             
417             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
418                 
419             if(echild){
420                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
421             }
422             
423             Roo.log('skipping render');
424             return cn;
425             
426         }
427         
428         var ret = false;
429         if (!build_from_html) {
430             return false;
431         }
432         
433         // this i think handles overlaying multiple children of the same type
434         // with the sam eelement.. - which might be buggy..
435         while (true) {
436             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437             
438             if (!echild) {
439                 break;
440             }
441             
442             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
443                 break;
444             }
445             
446             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
447         }
448        
449         return ret;
450     },
451     
452     
453     addxtypeChild : function (tree, cntr, is_body)
454     {
455         Roo.debug && Roo.log('addxtypeChild:' + cntr);
456         var cn = this;
457         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
458         
459         
460         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461                     (typeof(tree['flexy:foreach']) != 'undefined');
462           
463     
464         
465         skip_children = false;
466         // render the element if it's not BODY.
467         if (!is_body) {
468             
469             // if parent was disabled, then do not try and create the children..
470             if(!this[cntr](true)){
471                 tree.items = [];
472                 return tree;
473             }
474            
475             cn = Roo.factory(tree);
476            
477             cn.parentType = this.xtype; //??
478             cn.parentId = this.id;
479             
480             var build_from_html =  Roo.XComponent.build_from_html;
481             
482             
483             // does the container contain child eleemnts with 'xtype' attributes.
484             // that match this xtype..
485             // note - when we render we create these as well..
486             // so we should check to see if body has xtype set.
487             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
488                
489                 var self_cntr_el = Roo.get(this[cntr](false));
490                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
491                 if (echild) { 
492                     //Roo.log(Roo.XComponent.build_from_html);
493                     //Roo.log("got echild:");
494                     //Roo.log(echild);
495                 }
496                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497                 // and are not displayed -this causes this to use up the wrong element when matching.
498                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
499                 
500                 
501                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503                   
504                   
505                   
506                     cn.el = echild;
507                   //  Roo.log("GOT");
508                     //echild.dom.removeAttribute('xtype');
509                 } else {
510                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511                     Roo.debug && Roo.log(self_cntr_el);
512                     Roo.debug && Roo.log(echild);
513                     Roo.debug && Roo.log(cn);
514                 }
515             }
516            
517             
518            
519             // if object has flexy:if - then it may or may not be rendered.
520             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
521                 // skip a flexy if element.
522                 Roo.debug && Roo.log('skipping render');
523                 Roo.debug && Roo.log(tree);
524                 if (!cn.el) {
525                     Roo.debug && Roo.log('skipping all children');
526                     skip_children = true;
527                 }
528                 
529              } else {
530                  
531                 // actually if flexy:foreach is found, we really want to create 
532                 // multiple copies here...
533                 //Roo.log('render');
534                 //Roo.log(this[cntr]());
535                 // some elements do not have render methods.. like the layouts...
536                 /*
537                 if(this[cntr](true) === false){
538                     cn.items = [];
539                     return cn;
540                 }
541                 */
542                 cn.render && cn.render(this[cntr](true));
543                 
544              }
545             // then add the element..
546         }
547          
548         // handle the kids..
549         
550         var nitems = [];
551         /*
552         if (typeof (tree.menu) != 'undefined') {
553             tree.menu.parentType = cn.xtype;
554             tree.menu.triggerEl = cn.el;
555             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
556             
557         }
558         */
559         if (!tree.items || !tree.items.length) {
560             cn.items = nitems;
561             //Roo.log(["no children", this]);
562             
563             return cn;
564         }
565          
566         var items = tree.items;
567         delete tree.items;
568         
569         //Roo.log(items.length);
570             // add the items..
571         if (!skip_children) {    
572             for(var i =0;i < items.length;i++) {
573               //  Roo.log(['add child', items[i]]);
574                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575             }
576         }
577         
578         cn.items = nitems;
579         
580         //Roo.log("fire childrenrendered");
581         
582         cn.fireEvent('childrenrendered', this);
583         
584         return cn;
585     },
586     
587     /**
588      * Set the element that will be used to show or hide
589      */
590     setVisibilityEl : function(el)
591     {
592         this.visibilityEl = el;
593     },
594     
595      /**
596      * Get the element that will be used to show or hide
597      */
598     getVisibilityEl : function()
599     {
600         if (typeof(this.visibilityEl) == 'object') {
601             return this.visibilityEl;
602         }
603         
604         if (typeof(this.visibilityEl) == 'string') {
605             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
606         }
607         
608         return this.getEl();
609     },
610     
611     /**
612      * Show a component - removes 'hidden' class
613      */
614     show : function()
615     {
616         if(!this.getVisibilityEl()){
617             return;
618         }
619          
620         this.getVisibilityEl().removeClass(['hidden','d-none']);
621         
622         this.fireEvent('show', this);
623         
624         
625     },
626     /**
627      * Hide a component - adds 'hidden' class
628      */
629     hide: function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634         
635         this.getVisibilityEl().addClass(['hidden','d-none']);
636         
637         this.fireEvent('hide', this);
638         
639     }
640 });
641
642  /*
643  * - LGPL
644  *
645  * element
646  * 
647  */
648
649 /**
650  * @class Roo.bootstrap.Element
651  * @extends Roo.bootstrap.Component
652  * Bootstrap Element class
653  * @cfg {String} html contents of the element
654  * @cfg {String} tag tag of the element
655  * @cfg {String} cls class of the element
656  * @cfg {Boolean} preventDefault (true|false) default false
657  * @cfg {Boolean} clickable (true|false) default false
658  * @cfg {String} role default blank - set to button to force cursor pointer
659  
660  * 
661  * @constructor
662  * Create a new Element
663  * @param {Object} config The config object
664  */
665
666 Roo.bootstrap.Element = function(config){
667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
668     
669     this.addEvents({
670         // raw events
671         /**
672          * @event click
673          * When a element is chick
674          * @param {Roo.bootstrap.Element} this
675          * @param {Roo.EventObject} e
676          */
677         "click" : true 
678         
679       
680     });
681 };
682
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
684     
685     tag: 'div',
686     cls: '',
687     html: '',
688     preventDefault: false, 
689     clickable: false,
690     tapedTwice : false,
691     role : false,
692     
693     getAutoCreate : function(){
694         
695         var cfg = {
696             tag: this.tag,
697             // cls: this.cls, double assign in parent class Component.js :: onRender
698             html: this.html
699         };
700         if (this.role !== false) {
701             cfg.role = this.role;
702         }
703         
704         return cfg;
705     },
706     
707     initEvents: function() 
708     {
709         Roo.bootstrap.Element.superclass.initEvents.call(this);
710         
711         if(this.clickable){
712             this.el.on('click', this.onClick, this);
713         }
714         
715         
716     },
717     
718     onClick : function(e)
719     {
720         if(this.preventDefault){
721             e.preventDefault();
722         }
723         
724         this.fireEvent('click', this, e); // why was this double click before?
725     },
726     
727     
728     
729
730     
731     
732     getValue : function()
733     {
734         return this.el.dom.innerHTML;
735     },
736     
737     setValue : function(value)
738     {
739         this.el.dom.innerHTML = value;
740     }
741    
742 });
743
744  
745
746  /*
747  * - LGPL
748  *
749  * dropable area
750  * 
751  */
752
753 /**
754  * @class Roo.bootstrap.DropTarget
755  * @extends Roo.bootstrap.Element
756  * Bootstrap DropTarget class
757  
758  * @cfg {string} name dropable name
759  * 
760  * @constructor
761  * Create a new Dropable Area
762  * @param {Object} config The config object
763  */
764
765 Roo.bootstrap.DropTarget = function(config){
766     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767     
768     this.addEvents({
769         // raw events
770         /**
771          * @event click
772          * When a element is chick
773          * @param {Roo.bootstrap.Element} this
774          * @param {Roo.EventObject} e
775          */
776         "drop" : true
777     });
778 };
779
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
781     
782     
783     getAutoCreate : function(){
784         
785          
786     },
787     
788     initEvents: function() 
789     {
790         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
792             ddGroup: this.name,
793             listeners : {
794                 drop : this.dragDrop.createDelegate(this),
795                 enter : this.dragEnter.createDelegate(this),
796                 out : this.dragOut.createDelegate(this),
797                 over : this.dragOver.createDelegate(this)
798             }
799             
800         });
801         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
802     },
803     
804     dragDrop : function(source,e,data)
805     {
806         // user has to decide how to impliment this.
807         Roo.log('drop');
808         Roo.log(this);
809         //this.fireEvent('drop', this, source, e ,data);
810         return false;
811     },
812     
813     dragEnter : function(n, dd, e, data)
814     {
815         // probably want to resize the element to match the dropped element..
816         Roo.log("enter");
817         this.originalSize = this.el.getSize();
818         this.el.setSize( n.el.getSize());
819         this.dropZone.DDM.refreshCache(this.name);
820         Roo.log([n, dd, e, data]);
821     },
822     
823     dragOut : function(value)
824     {
825         // resize back to normal
826         Roo.log("out");
827         this.el.setSize(this.originalSize);
828         this.dropZone.resetConstraints();
829     },
830     
831     dragOver : function()
832     {
833         // ??? do nothing?
834     }
835    
836 });
837
838  
839
840  /*
841  * - LGPL
842  *
843  * Body
844  *
845  */
846
847 /**
848  * @class Roo.bootstrap.Body
849  * @extends Roo.bootstrap.Component
850  * Bootstrap Body class
851  *
852  * @constructor
853  * Create a new body
854  * @param {Object} config The config object
855  */
856
857 Roo.bootstrap.Body = function(config){
858
859     config = config || {};
860
861     Roo.bootstrap.Body.superclass.constructor.call(this, config);
862     this.el = Roo.get(config.el ? config.el : document.body );
863     if (this.cls && this.cls.length) {
864         Roo.get(document.body).addClass(this.cls);
865     }
866 };
867
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
869
870     is_body : true,// just to make sure it's constructed?
871
872         autoCreate : {
873         cls: 'container'
874     },
875     onRender : function(ct, position)
876     {
877        /* Roo.log("Roo.bootstrap.Body - onRender");
878         if (this.cls && this.cls.length) {
879             Roo.get(document.body).addClass(this.cls);
880         }
881         // style??? xttr???
882         */
883     }
884
885
886
887
888 });
889 /*
890  * - LGPL
891  *
892  * button group
893  * 
894  */
895
896
897 /**
898  * @class Roo.bootstrap.ButtonGroup
899  * @extends Roo.bootstrap.Component
900  * Bootstrap ButtonGroup class
901  * @cfg {String} size lg | sm | xs (default empty normal)
902  * @cfg {String} align vertical | justified  (default none)
903  * @cfg {String} direction up | down (default down)
904  * @cfg {Boolean} toolbar false | true
905  * @cfg {Boolean} btn true | false
906  * 
907  * 
908  * @constructor
909  * Create a new Input
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.ButtonGroup = function(config){
914     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
918     
919     size: '',
920     align: '',
921     direction: '',
922     toolbar: false,
923     btn: true,
924
925     getAutoCreate : function(){
926         var cfg = {
927             cls: 'btn-group',
928             html : null
929         };
930         
931         cfg.html = this.html || cfg.html;
932         
933         if (this.toolbar) {
934             cfg = {
935                 cls: 'btn-toolbar',
936                 html: null
937             };
938             
939             return cfg;
940         }
941         
942         if (['vertical','justified'].indexOf(this.align)!==-1) {
943             cfg.cls = 'btn-group-' + this.align;
944             
945             if (this.align == 'justified') {
946                 console.log(this.items);
947             }
948         }
949         
950         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951             cfg.cls += ' btn-group-' + this.size;
952         }
953         
954         if (this.direction == 'up') {
955             cfg.cls += ' dropup' ;
956         }
957         
958         return cfg;
959     },
960     /**
961      * Add a button to the group (similar to NavItem API.)
962      */
963     addItem : function(cfg)
964     {
965         var cn = new Roo.bootstrap.Button(cfg);
966         //this.register(cn);
967         cn.parentId = this.id;
968         cn.onRender(this.el, null);
969         return cn;
970     }
971    
972 });
973
974  /*
975  * - LGPL
976  *
977  * button
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Button
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Button class
985  * @cfg {String} html The button content
986  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989  * @cfg {String} size (lg|sm|xs)
990  * @cfg {String} tag (a|input|submit)
991  * @cfg {String} href empty or href
992  * @cfg {Boolean} disabled default false;
993  * @cfg {Boolean} isClose default false;
994  * @cfg {String} glyphicon depricated - use fa
995  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996  * @cfg {String} badge text for badge
997  * @cfg {String} theme (default|glow)  
998  * @cfg {Boolean} inverse dark themed version
999  * @cfg {Boolean} toggle is it a slidy toggle button
1000  * @cfg {Boolean} pressed   default null - if the button ahs active state
1001  * @cfg {String} ontext text for on slidy toggle state
1002  * @cfg {String} offtext text for off slidy toggle state
1003  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1004  * @cfg {Boolean} removeClass remove the standard class..
1005  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1006  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1007  * 
1008  * @constructor
1009  * Create a new button
1010  * @param {Object} config The config object
1011  */
1012
1013
1014 Roo.bootstrap.Button = function(config){
1015     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019         /**
1020          * @event click
1021          * When a button is pressed
1022          * @param {Roo.bootstrap.Button} btn
1023          * @param {Roo.EventObject} e
1024          */
1025         "click" : true,
1026         /**
1027          * @event dblclick
1028          * When a button is double clicked
1029          * @param {Roo.bootstrap.Button} btn
1030          * @param {Roo.EventObject} e
1031          */
1032         "dblclick" : true,
1033          /**
1034          * @event toggle
1035          * After the button has been toggles
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          * @param {boolean} pressed (also available as button.pressed)
1039          */
1040         "toggle" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1045     html: false,
1046     active: false,
1047     weight: '',
1048     badge_weight: '',
1049     outline : false,
1050     size: '',
1051     tag: 'button',
1052     href: '',
1053     disabled: false,
1054     isClose: false,
1055     glyphicon: '',
1056     fa: '',
1057     badge: '',
1058     theme: 'default',
1059     inverse: false,
1060     
1061     toggle: false,
1062     ontext: 'ON',
1063     offtext: 'OFF',
1064     defaulton: true,
1065     preventDefault: true,
1066     removeClass: false,
1067     name: false,
1068     target: false,
1069     group : false,
1070      
1071     pressed : null,
1072      
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : 'button',
1078             cls : 'roo-button',
1079             html: ''
1080         };
1081         
1082         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084             this.tag = 'button';
1085         } else {
1086             cfg.tag = this.tag;
1087         }
1088         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1089         
1090         if (this.toggle == true) {
1091             cfg={
1092                 tag: 'div',
1093                 cls: 'slider-frame roo-button',
1094                 cn: [
1095                     {
1096                         tag: 'span',
1097                         'data-on-text':'ON',
1098                         'data-off-text':'OFF',
1099                         cls: 'slider-button',
1100                         html: this.offtext
1101                     }
1102                 ]
1103             };
1104             // why are we validating the weights?
1105             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106                 cfg.cls +=  ' ' + this.weight;
1107             }
1108             
1109             return cfg;
1110         }
1111         
1112         if (this.isClose) {
1113             cfg.cls += ' close';
1114             
1115             cfg["aria-hidden"] = true;
1116             
1117             cfg.html = "&times;";
1118             
1119             return cfg;
1120         }
1121              
1122         
1123         if (this.theme==='default') {
1124             cfg.cls = 'btn roo-button';
1125             
1126             //if (this.parentType != 'Navbar') {
1127             this.weight = this.weight.length ?  this.weight : 'default';
1128             //}
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 
1131                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133                 cfg.cls += ' btn-' + outline + weight;
1134                 if (this.weight == 'default') {
1135                     // BC
1136                     cfg.cls += ' btn-' + this.weight;
1137                 }
1138             }
1139         } else if (this.theme==='glow') {
1140             
1141             cfg.tag = 'a';
1142             cfg.cls = 'btn-glow roo-button';
1143             
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 cfg.cls += ' ' + this.weight;
1147             }
1148         }
1149    
1150         
1151         if (this.inverse) {
1152             this.cls += ' inverse';
1153         }
1154         
1155         
1156         if (this.active || this.pressed === true) {
1157             cfg.cls += ' active';
1158         }
1159         
1160         if (this.disabled) {
1161             cfg.disabled = 'disabled';
1162         }
1163         
1164         if (this.items) {
1165             Roo.log('changing to ul' );
1166             cfg.tag = 'ul';
1167             this.glyphicon = 'caret';
1168             if (Roo.bootstrap.version == 4) {
1169                 this.fa = 'caret-down';
1170             }
1171             
1172         }
1173         
1174         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1175          
1176         //gsRoo.log(this.parentType);
1177         if (this.parentType === 'Navbar' && !this.parent().bar) {
1178             Roo.log('changing to li?');
1179             
1180             cfg.tag = 'li';
1181             
1182             cfg.cls = '';
1183             cfg.cn =  [{
1184                 tag : 'a',
1185                 cls : 'roo-button',
1186                 html : this.html,
1187                 href : this.href || '#'
1188             }];
1189             if (this.menu) {
1190                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1191                 cfg.cls += ' dropdown';
1192             }   
1193             
1194             delete cfg.html;
1195             
1196         }
1197         
1198        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1199         
1200         if (this.glyphicon) {
1201             cfg.html = ' ' + cfg.html;
1202             
1203             cfg.cn = [
1204                 {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon
1207                 }
1208             ];
1209         }
1210         if (this.fa) {
1211             cfg.html = ' ' + cfg.html;
1212             
1213             cfg.cn = [
1214                 {
1215                     tag: 'i',
1216                     cls: 'fa fas fa-' + this.fa
1217                 }
1218             ];
1219         }
1220         
1221         if (this.badge) {
1222             cfg.html += ' ';
1223             
1224             cfg.tag = 'a';
1225             
1226 //            cfg.cls='btn roo-button';
1227             
1228             cfg.href=this.href;
1229             
1230             var value = cfg.html;
1231             
1232             if(this.glyphicon){
1233                 value = {
1234                     tag: 'span',
1235                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1236                     html: this.html
1237                 };
1238             }
1239             if(this.fa){
1240                 value = {
1241                     tag: 'i',
1242                     cls: 'fa fas fa-' + this.fa,
1243                     html: this.html
1244                 };
1245             }
1246             
1247             var bw = this.badge_weight.length ? this.badge_weight :
1248                 (this.weight.length ? this.weight : 'secondary');
1249             bw = bw == 'default' ? 'secondary' : bw;
1250             
1251             cfg.cn = [
1252                 value,
1253                 {
1254                     tag: 'span',
1255                     cls: 'badge badge-' + bw,
1256                     html: this.badge
1257                 }
1258             ];
1259             
1260             cfg.html='';
1261         }
1262         
1263         if (this.menu) {
1264             cfg.cls += ' dropdown';
1265             cfg.html = typeof(cfg.html) != 'undefined' ?
1266                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1267         }
1268         
1269         if (cfg.tag !== 'a' && this.href !== '') {
1270             throw "Tag must be a to set href.";
1271         } else if (this.href.length > 0) {
1272             cfg.href = this.href;
1273         }
1274         
1275         if(this.removeClass){
1276             cfg.cls = '';
1277         }
1278         
1279         if(this.target){
1280             cfg.target = this.target;
1281         }
1282         
1283         return cfg;
1284     },
1285     initEvents: function() {
1286        // Roo.log('init events?');
1287 //        Roo.log(this.el.dom);
1288         // add the menu...
1289         
1290         if (typeof (this.menu) != 'undefined') {
1291             this.menu.parentType = this.xtype;
1292             this.menu.triggerEl = this.el;
1293             this.addxtype(Roo.apply({}, this.menu));
1294         }
1295
1296
1297         if (this.el.hasClass('roo-button')) {
1298              this.el.on('click', this.onClick, this);
1299              this.el.on('dblclick', this.onDblClick, this);
1300         } else {
1301              this.el.select('.roo-button').on('click', this.onClick, this);
1302              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1303              
1304         }
1305         // why?
1306         if(this.removeClass){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310         if (this.group === true) {
1311              if (this.pressed === false || this.pressed === true) {
1312                 // nothing
1313             } else {
1314                 this.pressed = false;
1315                 this.setActive(this.pressed);
1316             }
1317             
1318         }
1319         
1320         this.el.enableDisplayMode();
1321         
1322     },
1323     onClick : function(e)
1324     {
1325         if (this.disabled) {
1326             return;
1327         }
1328         
1329         Roo.log('button on click ');
1330         if(this.preventDefault){
1331             e.preventDefault();
1332         }
1333         
1334         if (this.group) {
1335             if (this.pressed) {
1336                 // do nothing -
1337                 return;
1338             }
1339             this.setActive(true);
1340             var pi = this.parent().items;
1341             for (var i = 0;i < pi.length;i++) {
1342                 if (this == pi[i]) {
1343                     continue;
1344                 }
1345                 if (pi[i].el.hasClass('roo-button')) {
1346                     pi[i].setActive(false);
1347                 }
1348             }
1349             this.fireEvent('click', this, e);            
1350             return;
1351         }
1352         
1353         if (this.pressed === true || this.pressed === false) {
1354             this.toggleActive(e);
1355         }
1356         
1357         
1358         this.fireEvent('click', this, e);
1359     },
1360     onDblClick: function(e)
1361     {
1362         if (this.disabled) {
1363             return;
1364         }
1365         if(this.preventDefault){
1366             e.preventDefault();
1367         }
1368         this.fireEvent('dblclick', this, e);
1369     },
1370     /**
1371      * Enables this button
1372      */
1373     enable : function()
1374     {
1375         this.disabled = false;
1376         this.el.removeClass('disabled');
1377         this.el.dom.removeAttribute("disabled");
1378     },
1379     
1380     /**
1381      * Disable this button
1382      */
1383     disable : function()
1384     {
1385         this.disabled = true;
1386         this.el.addClass('disabled');
1387         this.el.attr("disabled", "disabled")
1388     },
1389      /**
1390      * sets the active state on/off, 
1391      * @param {Boolean} state (optional) Force a particular state
1392      */
1393     setActive : function(v) {
1394         
1395         this.el[v ? 'addClass' : 'removeClass']('active');
1396         this.pressed = v;
1397     },
1398      /**
1399      * toggles the current active state 
1400      */
1401     toggleActive : function(e)
1402     {
1403         this.setActive(!this.pressed); // this modifies pressed...
1404         this.fireEvent('toggle', this, e, this.pressed);
1405     },
1406      /**
1407      * get the current active state
1408      * @return {boolean} true if it's active
1409      */
1410     isActive : function()
1411     {
1412         return this.el.hasClass('active');
1413     },
1414     /**
1415      * set the text of the first selected button
1416      */
1417     setText : function(str)
1418     {
1419         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1420     },
1421     /**
1422      * get the text of the first selected button
1423      */
1424     getText : function()
1425     {
1426         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1427     },
1428     
1429     setWeight : function(str)
1430     {
1431         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1433         this.weight = str;
1434         var outline = this.outline ? 'outline-' : '';
1435         if (str == 'default') {
1436             this.el.addClass('btn-default btn-outline-secondary');        
1437             return;
1438         }
1439         this.el.addClass('btn-' + outline + str);        
1440     }
1441     
1442     
1443 });
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1445
1446 Roo.bootstrap.Button.weights = [
1447     'default',
1448     'secondary' ,
1449     'primary',
1450     'success',
1451     'info',
1452     'warning',
1453     'danger',
1454     'link',
1455     'light',
1456     'dark'              
1457    
1458 ];/*
1459  * - LGPL
1460  *
1461  * column
1462  * 
1463  */
1464
1465 /**
1466  * @class Roo.bootstrap.Column
1467  * @extends Roo.bootstrap.Component
1468  * Bootstrap Column class
1469  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1477  *
1478  * 
1479  * @cfg {Boolean} hidden (true|false) hide the element
1480  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481  * @cfg {String} fa (ban|check|...) font awesome icon
1482  * @cfg {Number} fasize (1|2|....) font awsome size
1483
1484  * @cfg {String} icon (info-sign|check|...) glyphicon name
1485
1486  * @cfg {String} html content of column.
1487  * 
1488  * @constructor
1489  * Create a new Column
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Column = function(config){
1494     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1495 };
1496
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1498     
1499     xs: false,
1500     sm: false,
1501     md: false,
1502     lg: false,
1503     xsoff: false,
1504     smoff: false,
1505     mdoff: false,
1506     lgoff: false,
1507     html: '',
1508     offset: 0,
1509     alert: false,
1510     fa: false,
1511     icon : false,
1512     hidden : false,
1513     fasize : 1,
1514     
1515     getAutoCreate : function(){
1516         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1517         
1518         cfg = {
1519             tag: 'div',
1520             cls: 'column'
1521         };
1522         
1523         var settings=this;
1524         var sizes =   ['xs','sm','md','lg'];
1525         sizes.map(function(size ,ix){
1526             //Roo.log( size + ':' + settings[size]);
1527             
1528             if (settings[size+'off'] !== false) {
1529                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1530             }
1531             
1532             if (settings[size] === false) {
1533                 return;
1534             }
1535             
1536             if (!settings[size]) { // 0 = hidden
1537                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1538                 // bootsrap4
1539                 for (var i = ix; i > -1; i--) {
1540                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1541                 }
1542                 
1543                 
1544                 return;
1545             }
1546             cfg.cls += ' col-' + size + '-' + settings[size] + (
1547                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548             );
1549             
1550         });
1551         
1552         if (this.hidden) {
1553             cfg.cls += ' hidden';
1554         }
1555         
1556         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557             cfg.cls +=' alert alert-' + this.alert;
1558         }
1559         
1560         
1561         if (this.html.length) {
1562             cfg.html = this.html;
1563         }
1564         if (this.fa) {
1565             var fasize = '';
1566             if (this.fasize > 1) {
1567                 fasize = ' fa-' + this.fasize + 'x';
1568             }
1569             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1570             
1571             
1572         }
1573         if (this.icon) {
1574             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1575         }
1576         
1577         return cfg;
1578     }
1579    
1580 });
1581
1582  
1583
1584  /*
1585  * - LGPL
1586  *
1587  * page container.
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.Container
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Container class
1596  * @cfg {Boolean} jumbotron is it a jumbotron element
1597  * @cfg {String} html content of element
1598  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1600  * @cfg {String} header content of header (for panel)
1601  * @cfg {String} footer content of footer (for panel)
1602  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603  * @cfg {String} tag (header|aside|section) type of HTML tag.
1604  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605  * @cfg {String} fa font awesome icon
1606  * @cfg {String} icon (info-sign|check|...) glyphicon name
1607  * @cfg {Boolean} hidden (true|false) hide the element
1608  * @cfg {Boolean} expandable (true|false) default false
1609  * @cfg {Boolean} expanded (true|false) default true
1610  * @cfg {String} rheader contet on the right of header
1611  * @cfg {Boolean} clickable (true|false) default false
1612
1613  *     
1614  * @constructor
1615  * Create a new Container
1616  * @param {Object} config The config object
1617  */
1618
1619 Roo.bootstrap.Container = function(config){
1620     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621     
1622     this.addEvents({
1623         // raw events
1624          /**
1625          * @event expand
1626          * After the panel has been expand
1627          * 
1628          * @param {Roo.bootstrap.Container} this
1629          */
1630         "expand" : true,
1631         /**
1632          * @event collapse
1633          * After the panel has been collapsed
1634          * 
1635          * @param {Roo.bootstrap.Container} this
1636          */
1637         "collapse" : true,
1638         /**
1639          * @event click
1640          * When a element is chick
1641          * @param {Roo.bootstrap.Container} this
1642          * @param {Roo.EventObject} e
1643          */
1644         "click" : true
1645     });
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1649     
1650     jumbotron : false,
1651     well: '',
1652     panel : '',
1653     header: '',
1654     footer : '',
1655     sticky: '',
1656     tag : false,
1657     alert : false,
1658     fa: false,
1659     icon : false,
1660     expandable : false,
1661     rheader : '',
1662     expanded : true,
1663     clickable: false,
1664   
1665      
1666     getChildContainer : function() {
1667         
1668         if(!this.el){
1669             return false;
1670         }
1671         
1672         if (this.panel.length) {
1673             return this.el.select('.panel-body',true).first();
1674         }
1675         
1676         return this.el;
1677     },
1678     
1679     
1680     getAutoCreate : function(){
1681         
1682         var cfg = {
1683             tag : this.tag || 'div',
1684             html : '',
1685             cls : ''
1686         };
1687         if (this.jumbotron) {
1688             cfg.cls = 'jumbotron';
1689         }
1690         
1691         
1692         
1693         // - this is applied by the parent..
1694         //if (this.cls) {
1695         //    cfg.cls = this.cls + '';
1696         //}
1697         
1698         if (this.sticky.length) {
1699             
1700             var bd = Roo.get(document.body);
1701             if (!bd.hasClass('bootstrap-sticky')) {
1702                 bd.addClass('bootstrap-sticky');
1703                 Roo.select('html',true).setStyle('height', '100%');
1704             }
1705              
1706             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1707         }
1708         
1709         
1710         if (this.well.length) {
1711             switch (this.well) {
1712                 case 'lg':
1713                 case 'sm':
1714                     cfg.cls +=' well well-' +this.well;
1715                     break;
1716                 default:
1717                     cfg.cls +=' well';
1718                     break;
1719             }
1720         }
1721         
1722         if (this.hidden) {
1723             cfg.cls += ' hidden';
1724         }
1725         
1726         
1727         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728             cfg.cls +=' alert alert-' + this.alert;
1729         }
1730         
1731         var body = cfg;
1732         
1733         if (this.panel.length) {
1734             cfg.cls += ' panel panel-' + this.panel;
1735             cfg.cn = [];
1736             if (this.header.length) {
1737                 
1738                 var h = [];
1739                 
1740                 if(this.expandable){
1741                     
1742                     cfg.cls = cfg.cls + ' expandable';
1743                     
1744                     h.push({
1745                         tag: 'i',
1746                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1747                     });
1748                     
1749                 }
1750                 
1751                 h.push(
1752                     {
1753                         tag: 'span',
1754                         cls : 'panel-title',
1755                         html : (this.expandable ? '&nbsp;' : '') + this.header
1756                     },
1757                     {
1758                         tag: 'span',
1759                         cls: 'panel-header-right',
1760                         html: this.rheader
1761                     }
1762                 );
1763                 
1764                 cfg.cn.push({
1765                     cls : 'panel-heading',
1766                     style : this.expandable ? 'cursor: pointer' : '',
1767                     cn : h
1768                 });
1769                 
1770             }
1771             
1772             body = false;
1773             cfg.cn.push({
1774                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1775                 html : this.html
1776             });
1777             
1778             
1779             if (this.footer.length) {
1780                 cfg.cn.push({
1781                     cls : 'panel-footer',
1782                     html : this.footer
1783                     
1784                 });
1785             }
1786             
1787         }
1788         
1789         if (body) {
1790             body.html = this.html || cfg.html;
1791             // prefix with the icons..
1792             if (this.fa) {
1793                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1794             }
1795             if (this.icon) {
1796                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1797             }
1798             
1799             
1800         }
1801         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802             cfg.cls =  'container';
1803         }
1804         
1805         return cfg;
1806     },
1807     
1808     initEvents: function() 
1809     {
1810         if(this.expandable){
1811             var headerEl = this.headerEl();
1812         
1813             if(headerEl){
1814                 headerEl.on('click', this.onToggleClick, this);
1815             }
1816         }
1817         
1818         if(this.clickable){
1819             this.el.on('click', this.onClick, this);
1820         }
1821         
1822     },
1823     
1824     onToggleClick : function()
1825     {
1826         var headerEl = this.headerEl();
1827         
1828         if(!headerEl){
1829             return;
1830         }
1831         
1832         if(this.expanded){
1833             this.collapse();
1834             return;
1835         }
1836         
1837         this.expand();
1838     },
1839     
1840     expand : function()
1841     {
1842         if(this.fireEvent('expand', this)) {
1843             
1844             this.expanded = true;
1845             
1846             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1847             
1848             this.el.select('.panel-body',true).first().removeClass('hide');
1849             
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1857         }
1858         
1859     },
1860     
1861     collapse : function()
1862     {
1863         if(this.fireEvent('collapse', this)) {
1864             
1865             this.expanded = false;
1866             
1867             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868             this.el.select('.panel-body',true).first().addClass('hide');
1869         
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1877         }
1878     },
1879     
1880     toggleEl : function()
1881     {
1882         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1883             return;
1884         }
1885         
1886         return this.el.select('.panel-heading .fa',true).first();
1887     },
1888     
1889     headerEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading',true).first()
1896     },
1897     
1898     bodyEl : function()
1899     {
1900         if(!this.el || !this.panel.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-body',true).first()
1905     },
1906     
1907     titleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-title',true).first();
1914     },
1915     
1916     setTitle : function(v)
1917     {
1918         var titleEl = this.titleEl();
1919         
1920         if(!titleEl){
1921             return;
1922         }
1923         
1924         titleEl.dom.innerHTML = v;
1925     },
1926     
1927     getTitle : function()
1928     {
1929         
1930         var titleEl = this.titleEl();
1931         
1932         if(!titleEl){
1933             return '';
1934         }
1935         
1936         return titleEl.dom.innerHTML;
1937     },
1938     
1939     setRightTitle : function(v)
1940     {
1941         var t = this.el.select('.panel-header-right',true).first();
1942         
1943         if(!t){
1944             return;
1945         }
1946         
1947         t.dom.innerHTML = v;
1948     },
1949     
1950     onClick : function(e)
1951     {
1952         e.preventDefault();
1953         
1954         this.fireEvent('click', this, e);
1955     }
1956 });
1957
1958  /*
1959  *  - LGPL
1960  *
1961  *  This is BS4's Card element.. - similar to our containers probably..
1962  * 
1963  */
1964 /**
1965  * @class Roo.bootstrap.Card
1966  * @extends Roo.bootstrap.Component
1967  * Bootstrap Card class
1968  *
1969  *
1970  * possible... may not be implemented..
1971  * @cfg {String} header_image  src url of image.
1972  * @cfg {String|Object} header
1973  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1975  * 
1976  * @cfg {String} title
1977  * @cfg {String} subtitle
1978  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979  * @cfg {String} footer
1980  
1981  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1982  * 
1983  * @cfg {String} margin (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1990  *
1991  * @cfg {String} padding (0|1|2|3|4|5)
1992  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994  * @cfg {String} padding_left (0|1|2|3|4|5)
1995  * @cfg {String} padding_right (0|1|2|3|4|5)
1996  * @cfg {String} padding_x (0|1|2|3|4|5)
1997  * @cfg {String} padding_y (0|1|2|3|4|5)
1998  *
1999  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  
2005  * @config {Boolean} dragable  if this card can be dragged.
2006  * @config {String} drag_group  group for drag
2007  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2008  * @config {String} drop_group  group for drag
2009  * 
2010  * @config {Boolean} collapsable can the body be collapsed.
2011  * @config {Boolean} collapsed is the body collapsed when rendered...
2012  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013  * @config {Boolean} rotated is the body rotated when rendered...
2014  * 
2015  * @constructor
2016  * Create a new Container
2017  * @param {Object} config The config object
2018  */
2019
2020 Roo.bootstrap.Card = function(config){
2021     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022     
2023     this.addEvents({
2024          // raw events
2025         /**
2026          * @event drop
2027          * When a element a card is dropped
2028          * @param {Roo.bootstrap.Card} this
2029          *
2030          * 
2031          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032          * @param {String} position 'above' or 'below'
2033          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034         
2035          */
2036         'drop' : true,
2037          /**
2038          * @event rotate
2039          * When a element a card is rotate
2040          * @param {Roo.bootstrap.Card} this
2041          * @param {Roo.Element} n the node being dropped?
2042          * @param {Boolean} rotate status
2043          */
2044         'rotate' : true,
2045         /**
2046          * @event cardover
2047          * When a card element is dragged over ready to drop (return false to block dropable)
2048          * @param {Roo.bootstrap.Card} this
2049          * @param {Object} data from dragdrop 
2050          */
2051          'cardover' : true
2052          
2053     });
2054 };
2055
2056
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2058     
2059     
2060     weight : '',
2061     
2062     margin: '', /// may be better in component?
2063     margin_top: '', 
2064     margin_bottom: '', 
2065     margin_left: '',
2066     margin_right: '',
2067     margin_x: '',
2068     margin_y: '',
2069     
2070     padding : '',
2071     padding_top: '', 
2072     padding_bottom: '', 
2073     padding_left: '',
2074     padding_right: '',
2075     padding_x: '',
2076     padding_y: '',
2077     
2078     display: '', 
2079     display_xs: '', 
2080     display_sm: '', 
2081     display_lg: '',
2082     display_xl: '',
2083  
2084     header_image  : '',
2085     header : '',
2086     header_size : 0,
2087     title : '',
2088     subtitle : '',
2089     html : '',
2090     footer: '',
2091
2092     collapsable : false,
2093     collapsed : false,
2094     rotateable : false,
2095     rotated : false,
2096     
2097     dragable : false,
2098     drag_group : false,
2099     dropable : false,
2100     drop_group : false,
2101     childContainer : false,
2102     dropEl : false, /// the dom placeholde element that indicates drop location.
2103     containerEl: false, // body container
2104     bodyEl: false, // card-body
2105     headerContainerEl : false, //
2106     headerEl : false,
2107     header_imageEl : false,
2108     
2109     
2110     layoutCls : function()
2111     {
2112         var cls = '';
2113         var t = this;
2114         Roo.log(this.margin_bottom.length);
2115         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2117             
2118             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2120             }
2121             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2123             }
2124         });
2125         
2126         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2129             }
2130         });
2131         
2132         // more generic support?
2133         if (this.hidden) {
2134             cls += ' d-none';
2135         }
2136         
2137         return cls;
2138     },
2139  
2140        // Roo.log("Call onRender: " + this.xtype);
2141         /*  We are looking at something like this.
2142 <div class="card">
2143     <img src="..." class="card-img-top" alt="...">
2144     <div class="card-body">
2145         <h5 class="card-title">Card title</h5>
2146          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2147
2148         >> this bit is really the body...
2149         <div> << we will ad dthis in hopefully it will not break shit.
2150         
2151         ** card text does not actually have any styling...
2152         
2153             <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>
2154         
2155         </div> <<
2156           <a href="#" class="card-link">Card link</a>
2157           
2158     </div>
2159     <div class="card-footer">
2160         <small class="text-muted">Last updated 3 mins ago</small>
2161     </div>
2162 </div>
2163          */
2164     getAutoCreate : function(){
2165         
2166         var cfg = {
2167             tag : 'div',
2168             cls : 'card',
2169             cn : [ ]
2170         };
2171         
2172         if (this.weight.length && this.weight != 'light') {
2173             cfg.cls += ' text-white';
2174         } else {
2175             cfg.cls += ' text-dark'; // need as it's nested..
2176         }
2177         if (this.weight.length) {
2178             cfg.cls += ' bg-' + this.weight;
2179         }
2180         
2181         cfg.cls += ' ' + this.layoutCls(); 
2182         
2183         var hdr = false;
2184         var hdr_ctr = false;
2185         if (this.header.length) {
2186             hdr = {
2187                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2189                 cn : []
2190             };
2191             cfg.cn.push(hdr);
2192             hdr_ctr = hdr;
2193         } else {
2194             hdr = {
2195                 tag : 'div',
2196                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197                 cn : []
2198             };
2199             cfg.cn.push(hdr);
2200             hdr_ctr = hdr;
2201         }
2202         if (this.collapsable) {
2203             hdr_ctr = {
2204             tag : 'a',
2205             cls : 'd-block user-select-none',
2206             cn: [
2207                     {
2208                         tag: 'i',
2209                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2210                     }
2211                    
2212                 ]
2213             };
2214             hdr.cn.push(hdr_ctr);
2215         }
2216         
2217         hdr_ctr.cn.push(        {
2218             tag: 'span',
2219             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2220             html : this.header
2221         });
2222         
2223         
2224         if (this.header_image.length) {
2225             cfg.cn.push({
2226                 tag : 'img',
2227                 cls : 'card-img-top',
2228                 src: this.header_image // escape?
2229             });
2230         } else {
2231             cfg.cn.push({
2232                     tag : 'div',
2233                     cls : 'card-img-top d-none' 
2234                 });
2235         }
2236             
2237         var body = {
2238             tag : 'div',
2239             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2240             cn : []
2241         };
2242         var obody = body;
2243         if (this.collapsable || this.rotateable) {
2244             obody = {
2245                 tag: 'div',
2246                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2247                 cn : [  body ]
2248             };
2249         }
2250         
2251         cfg.cn.push(obody);
2252         
2253         if (this.title.length) {
2254             body.cn.push({
2255                 tag : 'div',
2256                 cls : 'card-title',
2257                 src: this.title // escape?
2258             });
2259         }  
2260         
2261         if (this.subtitle.length) {
2262             body.cn.push({
2263                 tag : 'div',
2264                 cls : 'card-title',
2265                 src: this.subtitle // escape?
2266             });
2267         }
2268         
2269         body.cn.push({
2270             tag : 'div',
2271             cls : 'roo-card-body-ctr'
2272         });
2273         
2274         if (this.html.length) {
2275             body.cn.push({
2276                 tag: 'div',
2277                 html : this.html
2278             });
2279         }
2280         // fixme ? handle objects?
2281         
2282         if (this.footer.length) {
2283            
2284             cfg.cn.push({
2285                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2286                 html : this.footer
2287             });
2288             
2289         } else {
2290             cfg.cn.push({cls : 'card-footer d-none'});
2291         }
2292         
2293         // footer...
2294         
2295         return cfg;
2296     },
2297     
2298     
2299     getCardHeader : function()
2300     {
2301         var  ret = this.el.select('.card-header',true).first();
2302         if (ret.hasClass('d-none')) {
2303             ret.removeClass('d-none');
2304         }
2305         
2306         return ret;
2307     },
2308     getCardFooter : function()
2309     {
2310         var  ret = this.el.select('.card-footer',true).first();
2311         if (ret.hasClass('d-none')) {
2312             ret.removeClass('d-none');
2313         }
2314         
2315         return ret;
2316     },
2317     getCardImageTop : function()
2318     {
2319         var  ret = this.header_imageEl;
2320         if (ret.hasClass('d-none')) {
2321             ret.removeClass('d-none');
2322         }
2323             
2324         return ret;
2325     },
2326     
2327     getChildContainer : function()
2328     {
2329         
2330         if(!this.el){
2331             return false;
2332         }
2333         return this.el.select('.roo-card-body-ctr',true).first();    
2334     },
2335     
2336     initEvents: function() 
2337     {
2338         this.bodyEl = this.el.select('.card-body',true).first(); 
2339         this.containerEl = this.getChildContainer();
2340         if(this.dragable){
2341             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342                     containerScroll: true,
2343                     ddGroup: this.drag_group || 'default_card_drag_group'
2344             });
2345             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2346         }
2347         if (this.dropable) {
2348             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349                 containerScroll: true,
2350                 ddGroup: this.drop_group || 'default_card_drag_group'
2351             });
2352             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2357         }
2358         
2359         if (this.collapsable) {
2360             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2361         }
2362         if (this.rotateable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2364         }
2365         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2366          
2367         this.footerEl = this.el.select('.card-footer',true).first();
2368         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370         this.headerEl = this.el.select('.card-header',true).first();
2371         
2372         if (this.rotated) {
2373             this.el.addClass('roo-card-rotated');
2374             this.fireEvent('rotate', this, true);
2375         }
2376         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2377         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2378         
2379     },
2380     getDragData : function(e)
2381     {
2382         var target = this.getEl();
2383         if (target) {
2384             //this.handleSelection(e);
2385             
2386             var dragData = {
2387                 source: this,
2388                 copy: false,
2389                 nodes: this.getEl(),
2390                 records: []
2391             };
2392             
2393             
2394             dragData.ddel = target.dom ;    // the div element
2395             Roo.log(target.getWidth( ));
2396             dragData.ddel.style.width = target.getWidth() + 'px';
2397             
2398             return dragData;
2399         }
2400         return false;
2401     },
2402     /**
2403     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2404     *    whole Element becomes the target, and this causes the drop gesture to append.
2405     *
2406     *    Returns an object:
2407     *     {
2408            
2409            position : 'below' or 'above'
2410            card  : relateive to card OBJECT (or true for no cards listed)
2411            items_n : relative to nth item in list
2412            card_n : relative to  nth card in list
2413     }
2414     *
2415     *    
2416     */
2417     getTargetFromEvent : function(e, dragged_card_el)
2418     {
2419         var target = e.getTarget();
2420         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421             target = target.parentNode;
2422         }
2423         
2424         var ret = {
2425             position: '',
2426             cards : [],
2427             card_n : -1,
2428             items_n : -1,
2429             card : false 
2430         };
2431         
2432         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433         // see if target is one of the 'cards'...
2434         
2435         
2436         //Roo.log(this.items.length);
2437         var pos = false;
2438         
2439         var last_card_n = 0;
2440         var cards_len  = 0;
2441         for (var i = 0;i< this.items.length;i++) {
2442             
2443             if (!this.items[i].el.hasClass('card')) {
2444                  continue;
2445             }
2446             pos = this.getDropPoint(e, this.items[i].el.dom);
2447             
2448             cards_len = ret.cards.length;
2449             //Roo.log(this.items[i].el.dom.id);
2450             ret.cards.push(this.items[i]);
2451             last_card_n  = i;
2452             if (ret.card_n < 0 && pos == 'above') {
2453                 ret.position = cards_len > 0 ? 'below' : pos;
2454                 ret.items_n = i > 0 ? i - 1 : 0;
2455                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2456                 ret.card = ret.cards[ret.card_n];
2457             }
2458         }
2459         if (!ret.cards.length) {
2460             ret.card = true;
2461             ret.position = 'below';
2462             ret.items_n;
2463             return ret;
2464         }
2465         // could not find a card.. stick it at the end..
2466         if (ret.card_n < 0) {
2467             ret.card_n = last_card_n;
2468             ret.card = ret.cards[last_card_n];
2469             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470             ret.position = 'below';
2471         }
2472         
2473         if (this.items[ret.items_n].el == dragged_card_el) {
2474             return false;
2475         }
2476         
2477         if (ret.position == 'below') {
2478             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2479             
2480             if (card_after  && card_after.el == dragged_card_el) {
2481                 return false;
2482             }
2483             return ret;
2484         }
2485         
2486         // its's after ..
2487         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2488         
2489         if (card_before  && card_before.el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         return ret;
2494     },
2495     
2496     onNodeEnter : function(n, dd, e, data){
2497         return false;
2498     },
2499     onNodeOver : function(n, dd, e, data)
2500     {
2501        
2502         var target_info = this.getTargetFromEvent(e,data.source.el);
2503         if (target_info === false) {
2504             this.dropPlaceHolder('hide');
2505             return false;
2506         }
2507         Roo.log(['getTargetFromEvent', target_info ]);
2508         
2509         
2510         if (this.fireEvent('cardover', this, [ data ]) === false) {
2511             return false;
2512         }
2513         
2514         this.dropPlaceHolder('show', target_info,data);
2515         
2516         return false; 
2517     },
2518     onNodeOut : function(n, dd, e, data){
2519         this.dropPlaceHolder('hide');
2520      
2521     },
2522     onNodeDrop : function(n, dd, e, data)
2523     {
2524         
2525         // call drop - return false if
2526         
2527         // this could actually fail - if the Network drops..
2528         // we will ignore this at present..- client should probably reload
2529         // the whole set of cards if stuff like that fails.
2530         
2531         
2532         var info = this.getTargetFromEvent(e,data.source.el);
2533         if (info === false) {
2534             return false;
2535         }
2536         this.dropPlaceHolder('hide');
2537   
2538           
2539     
2540         this.acceptCard(data.source, info.position, info.card, info.items_n);
2541         return true;
2542          
2543     },
2544     firstChildCard : function()
2545     {
2546         for (var i = 0;i< this.items.length;i++) {
2547             
2548             if (!this.items[i].el.hasClass('card')) {
2549                  continue;
2550             }
2551             return this.items[i];
2552         }
2553         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2554     },
2555     /**
2556      * accept card
2557      *
2558      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2559      */
2560     acceptCard : function(move_card,  position, next_to_card )
2561     {
2562         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2563             return false;
2564         }
2565         
2566         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2567         
2568         move_card.parent().removeCard(move_card);
2569         
2570         
2571         var dom = move_card.el.dom;
2572         dom.style.width = ''; // clear with - which is set by drag.
2573         
2574         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575             var cardel = next_to_card.el.dom;
2576             
2577             if (position == 'above' ) {
2578                 cardel.parentNode.insertBefore(dom, cardel);
2579             } else if (cardel.nextSibling) {
2580                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2581             } else {
2582                 cardel.parentNode.append(dom);
2583             }
2584         } else {
2585             // card container???
2586             this.containerEl.dom.append(dom);
2587         }
2588         
2589         //FIXME HANDLE card = true 
2590         
2591         // add this to the correct place in items.
2592         
2593         // remove Card from items.
2594         
2595        
2596         if (this.items.length) {
2597             var nitems = [];
2598             //Roo.log([info.items_n, info.position, this.items.length]);
2599             for (var i =0; i < this.items.length; i++) {
2600                 if (i == to_items_n && position == 'above') {
2601                     nitems.push(move_card);
2602                 }
2603                 nitems.push(this.items[i]);
2604                 if (i == to_items_n && position == 'below') {
2605                     nitems.push(move_card);
2606                 }
2607             }
2608             this.items = nitems;
2609             Roo.log(this.items);
2610         } else {
2611             this.items.push(move_card);
2612         }
2613         
2614         move_card.parentId = this.id;
2615         
2616         return true;
2617         
2618         
2619     },
2620     removeCard : function(c)
2621     {
2622         this.items = this.items.filter(function(e) { return e != c });
2623  
2624         var dom = c.el.dom;
2625         dom.parentNode.removeChild(dom);
2626         dom.style.width = ''; // clear with - which is set by drag.
2627         c.parentId = false;
2628         
2629     },
2630     
2631     /**    Decide whether to drop above or below a View node. */
2632     getDropPoint : function(e, n, dd)
2633     {
2634         if (dd) {
2635              return false;
2636         }
2637         if (n == this.containerEl.dom) {
2638             return "above";
2639         }
2640         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641         var c = t + (b - t) / 2;
2642         var y = Roo.lib.Event.getPageY(e);
2643         if(y <= c) {
2644             return "above";
2645         }else{
2646             return "below";
2647         }
2648     },
2649     onToggleCollapse : function(e)
2650         {
2651         if (this.collapsed) {
2652             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653             this.collapsableEl.addClass('show');
2654             this.collapsed = false;
2655             return;
2656         }
2657         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658         this.collapsableEl.removeClass('show');
2659         this.collapsed = true;
2660         
2661     
2662     },
2663     
2664     onToggleRotate : function(e)
2665     {
2666         this.collapsableEl.removeClass('show');
2667         this.footerEl.removeClass('d-none');
2668         this.el.removeClass('roo-card-rotated');
2669         this.el.removeClass('d-none');
2670         if (this.rotated) {
2671             
2672             this.collapsableEl.addClass('show');
2673             this.rotated = false;
2674             this.fireEvent('rotate', this, this.rotated);
2675             return;
2676         }
2677         this.el.addClass('roo-card-rotated');
2678         this.footerEl.addClass('d-none');
2679         this.el.select('.roo-collapsable').removeClass('show');
2680         
2681         this.rotated = true;
2682         this.fireEvent('rotate', this, this.rotated);
2683     
2684     },
2685     
2686     dropPlaceHolder: function (action, info, data)
2687     {
2688         if (this.dropEl === false) {
2689             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2690             cls : 'd-none'
2691             },true);
2692         }
2693         this.dropEl.removeClass(['d-none', 'd-block']);        
2694         if (action == 'hide') {
2695             
2696             this.dropEl.addClass('d-none');
2697             return;
2698         }
2699         // FIXME - info.card == true!!!
2700         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2701         
2702         if (info.card !== true) {
2703             var cardel = info.card.el.dom;
2704             
2705             if (info.position == 'above') {
2706                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707             } else if (cardel.nextSibling) {
2708                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2709             } else {
2710                 cardel.parentNode.append(this.dropEl.dom);
2711             }
2712         } else {
2713             // card container???
2714             this.containerEl.dom.append(this.dropEl.dom);
2715         }
2716         
2717         this.dropEl.addClass('d-block roo-card-dropzone');
2718         
2719         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2720         
2721         
2722     
2723     
2724     
2725     },
2726     setHeaderText: function(html)
2727     {
2728         this.header = html;
2729         if (this.headerContainerEl) {
2730             this.headerContainerEl.dom.innerHTML = html;
2731         }
2732     },
2733     onHeaderImageLoad : function(ev, he)
2734     {
2735         if (!this.header_image_fit_square) {
2736             return;
2737         }
2738         
2739         var hw = he.naturalHeight / he.naturalWidth;
2740         // wide image = < 0
2741         // tall image = > 1
2742         //var w = he.dom.naturalWidth;
2743         var ww = he.width;
2744         he.style.left =  0;
2745         he.style.position =  'relative';
2746         if (hw > 1) {
2747             var nw = (ww * (1/hw));
2748             Roo.get(he).setSize( ww * (1/hw),  ww);
2749             he.style.left =  ((ww - nw)/ 2) + 'px';
2750             he.style.position =  'relative';
2751         }
2752
2753     }
2754
2755     
2756 });
2757
2758 /*
2759  * - LGPL
2760  *
2761  * Card header - holder for the card header elements.
2762  * 
2763  */
2764
2765 /**
2766  * @class Roo.bootstrap.CardHeader
2767  * @extends Roo.bootstrap.Element
2768  * Bootstrap CardHeader class
2769  * @constructor
2770  * Create a new Card Header - that you can embed children into
2771  * @param {Object} config The config object
2772  */
2773
2774 Roo.bootstrap.CardHeader = function(config){
2775     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2776 };
2777
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2779     
2780     
2781     container_method : 'getCardHeader' 
2782     
2783      
2784     
2785     
2786    
2787 });
2788
2789  
2790
2791  /*
2792  * - LGPL
2793  *
2794  * Card footer - holder for the card footer elements.
2795  * 
2796  */
2797
2798 /**
2799  * @class Roo.bootstrap.CardFooter
2800  * @extends Roo.bootstrap.Element
2801  * Bootstrap CardFooter class
2802  * @constructor
2803  * Create a new Card Footer - that you can embed children into
2804  * @param {Object} config The config object
2805  */
2806
2807 Roo.bootstrap.CardFooter = function(config){
2808     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2809 };
2810
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2812     
2813     
2814     container_method : 'getCardFooter' 
2815     
2816      
2817     
2818     
2819    
2820 });
2821
2822  
2823
2824  /*
2825  * - LGPL
2826  *
2827  * Card header - holder for the card header elements.
2828  * 
2829  */
2830
2831 /**
2832  * @class Roo.bootstrap.CardImageTop
2833  * @extends Roo.bootstrap.Element
2834  * Bootstrap CardImageTop class
2835  * @constructor
2836  * Create a new Card Image Top container
2837  * @param {Object} config The config object
2838  */
2839
2840 Roo.bootstrap.CardImageTop = function(config){
2841     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2842 };
2843
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2845     
2846    
2847     container_method : 'getCardImageTop' 
2848     
2849      
2850     
2851    
2852 });
2853
2854  
2855
2856  
2857 /*
2858 * Licence: LGPL
2859 */
2860
2861 /**
2862  * @class Roo.bootstrap.ButtonUploader
2863  * @extends Roo.bootstrap.Button
2864  * Bootstrap Button Uploader class - it's a button which when you add files to it
2865  *
2866  * 
2867  * @cfg {Number} errorTimeout default 3000
2868  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2869  * @cfg {Array}  html The button text.
2870  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2871  *
2872  * @constructor
2873  * Create a new CardUploader
2874  * @param {Object} config The config object
2875  */
2876
2877 Roo.bootstrap.ButtonUploader = function(config){
2878     
2879  
2880     
2881     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882     
2883      
2884      this.addEvents({
2885          // raw events
2886         /**
2887          * @event beforeselect
2888          * When button is pressed, before show upload files dialog is shown
2889          * @param {Roo.bootstrap.UploaderButton} this
2890          *
2891          */
2892         'beforeselect' : true,
2893          /**
2894          * @event fired when files have been selected, 
2895          * When a the download link is clicked
2896          * @param {Roo.bootstrap.UploaderButton} this
2897          * @param {Array} Array of files that have been uploaded
2898          */
2899         'uploaded' : true
2900         
2901     });
2902 };
2903  
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2905     
2906      
2907     errorTimeout : 3000,
2908      
2909     images : false,
2910    
2911     fileCollection : false,
2912     allowBlank : true,
2913     
2914     multiple : true,
2915     
2916     getAutoCreate : function()
2917     {
2918         var im = {
2919             tag: 'input',
2920             type : 'file',
2921             cls : 'd-none  roo-card-upload-selector' 
2922           
2923         };
2924         if (this.multiple) {
2925             im.multiple = 'multiple';
2926         }
2927         
2928         return  {
2929             cls :'div' ,
2930             cn : [
2931                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2932                 im
2933
2934             ]
2935         };
2936            
2937          
2938     },
2939      
2940    
2941     initEvents : function()
2942     {
2943         
2944         Roo.bootstrap.Button.prototype.initEvents.call(this);
2945         
2946         
2947         
2948         
2949         
2950         this.urlAPI = (window.createObjectURL && window) || 
2951                                 (window.URL && URL.revokeObjectURL && URL) || 
2952                                 (window.webkitURL && webkitURL);
2953                         
2954          
2955          
2956          
2957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2958         
2959         this.selectorEl.on('change', this.onFileSelected, this);
2960          
2961          
2962        
2963     },
2964     
2965    
2966     onClick : function(e)
2967     {
2968         e.preventDefault();
2969         
2970         if ( this.fireEvent('beforeselect', this) === false) {
2971             return;
2972         }
2973          
2974         this.selectorEl.dom.click();
2975          
2976     },
2977     
2978     onFileSelected : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2983             return;
2984         }
2985         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986         this.selectorEl.dom.value  = '';// hopefully reset..
2987         
2988         this.fireEvent('uploaded', this,  files );
2989         
2990     },
2991     
2992        
2993    
2994     
2995     /**
2996      * addCard - add an Attachment to the uploader
2997      * @param data - the data about the image to upload
2998      *
2999      * {
3000           id : 123
3001           title : "Title of file",
3002           is_uploaded : false,
3003           src : "http://.....",
3004           srcfile : { the File upload object },
3005           mimetype : file.type,
3006           preview : false,
3007           is_deleted : 0
3008           .. any other data...
3009         }
3010      *
3011      * 
3012     */
3013      
3014     reset: function()
3015     {
3016          
3017          this.selectorEl
3018     } 
3019     
3020     
3021     
3022     
3023 });
3024  /*
3025  * - LGPL
3026  *
3027  * image
3028  * 
3029  */
3030
3031
3032 /**
3033  * @class Roo.bootstrap.Img
3034  * @extends Roo.bootstrap.Component
3035  * Bootstrap Img class
3036  * @cfg {Boolean} imgResponsive false | true
3037  * @cfg {String} border rounded | circle | thumbnail
3038  * @cfg {String} src image source
3039  * @cfg {String} alt image alternative text
3040  * @cfg {String} href a tag href
3041  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042  * @cfg {String} xsUrl xs image source
3043  * @cfg {String} smUrl sm image source
3044  * @cfg {String} mdUrl md image source
3045  * @cfg {String} lgUrl lg image source
3046  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3047  * 
3048  * @constructor
3049  * Create a new Input
3050  * @param {Object} config The config object
3051  */
3052
3053 Roo.bootstrap.Img = function(config){
3054     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3055     
3056     this.addEvents({
3057         // img events
3058         /**
3059          * @event click
3060          * The img click event for the img.
3061          * @param {Roo.EventObject} e
3062          */
3063         "click" : true,
3064         /**
3065          * @event load
3066          * The when any image loads
3067          * @param {Roo.EventObject} e
3068          */
3069         "load" : true
3070     });
3071 };
3072
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3074     
3075     imgResponsive: true,
3076     border: '',
3077     src: 'about:blank',
3078     href: false,
3079     target: false,
3080     xsUrl: '',
3081     smUrl: '',
3082     mdUrl: '',
3083     lgUrl: '',
3084     backgroundContain : false,
3085
3086     getAutoCreate : function()
3087     {   
3088         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089             return this.createSingleImg();
3090         }
3091         
3092         var cfg = {
3093             tag: 'div',
3094             cls: 'roo-image-responsive-group',
3095             cn: []
3096         };
3097         var _this = this;
3098         
3099         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3100             
3101             if(!_this[size + 'Url']){
3102                 return;
3103             }
3104             
3105             var img = {
3106                 tag: 'img',
3107                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108                 html: _this.html || cfg.html,
3109                 src: _this[size + 'Url']
3110             };
3111             
3112             img.cls += ' roo-image-responsive-' + size;
3113             
3114             var s = ['xs', 'sm', 'md', 'lg'];
3115             
3116             s.splice(s.indexOf(size), 1);
3117             
3118             Roo.each(s, function(ss){
3119                 img.cls += ' hidden-' + ss;
3120             });
3121             
3122             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123                 cfg.cls += ' img-' + _this.border;
3124             }
3125             
3126             if(_this.alt){
3127                 cfg.alt = _this.alt;
3128             }
3129             
3130             if(_this.href){
3131                 var a = {
3132                     tag: 'a',
3133                     href: _this.href,
3134                     cn: [
3135                         img
3136                     ]
3137                 };
3138
3139                 if(this.target){
3140                     a.target = _this.target;
3141                 }
3142             }
3143             
3144             cfg.cn.push((_this.href) ? a : img);
3145             
3146         });
3147         
3148         return cfg;
3149     },
3150     
3151     createSingleImg : function()
3152     {
3153         var cfg = {
3154             tag: 'img',
3155             cls: (this.imgResponsive) ? 'img-responsive' : '',
3156             html : null,
3157             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3158         };
3159         
3160         if (this.backgroundContain) {
3161             cfg.cls += ' background-contain';
3162         }
3163         
3164         cfg.html = this.html || cfg.html;
3165         
3166         if (this.backgroundContain) {
3167             cfg.style="background-image: url(" + this.src + ')';
3168         } else {
3169             cfg.src = this.src || cfg.src;
3170         }
3171         
3172         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173             cfg.cls += ' img-' + this.border;
3174         }
3175         
3176         if(this.alt){
3177             cfg.alt = this.alt;
3178         }
3179         
3180         if(this.href){
3181             var a = {
3182                 tag: 'a',
3183                 href: this.href,
3184                 cn: [
3185                     cfg
3186                 ]
3187             };
3188             
3189             if(this.target){
3190                 a.target = this.target;
3191             }
3192             
3193         }
3194         
3195         return (this.href) ? a : cfg;
3196     },
3197     
3198     initEvents: function() 
3199     {
3200         if(!this.href){
3201             this.el.on('click', this.onClick, this);
3202         }
3203         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204             this.el.on('load', this.onImageLoad, this);
3205         } else {
3206             // not sure if this works.. not tested
3207             this.el.select('img', true).on('load', this.onImageLoad, this);
3208         }
3209         
3210     },
3211     
3212     onClick : function(e)
3213     {
3214         Roo.log('img onclick');
3215         this.fireEvent('click', this, e);
3216     },
3217     onImageLoad: function(e)
3218     {
3219         Roo.log('img load');
3220         this.fireEvent('load', this, e);
3221     },
3222     
3223     /**
3224      * Sets the url of the image - used to update it
3225      * @param {String} url the url of the image
3226      */
3227     
3228     setSrc : function(url)
3229     {
3230         this.src =  url;
3231         
3232         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233             if (this.backgroundContain) {
3234                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3235             } else {
3236                 this.el.dom.src =  url;
3237             }
3238             return;
3239         }
3240         
3241         this.el.select('img', true).first().dom.src =  url;
3242     }
3243     
3244     
3245    
3246 });
3247
3248  /*
3249  * - LGPL
3250  *
3251  * image
3252  * 
3253  */
3254
3255
3256 /**
3257  * @class Roo.bootstrap.Link
3258  * @extends Roo.bootstrap.Component
3259  * Bootstrap Link Class
3260  * @cfg {String} alt image alternative text
3261  * @cfg {String} href a tag href
3262  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263  * @cfg {String} html the content of the link.
3264  * @cfg {String} anchor name for the anchor link
3265  * @cfg {String} fa - favicon
3266
3267  * @cfg {Boolean} preventDefault (true | false) default false
3268
3269  * 
3270  * @constructor
3271  * Create a new Input
3272  * @param {Object} config The config object
3273  */
3274
3275 Roo.bootstrap.Link = function(config){
3276     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3277     
3278     this.addEvents({
3279         // img events
3280         /**
3281          * @event click
3282          * The img click event for the img.
3283          * @param {Roo.EventObject} e
3284          */
3285         "click" : true
3286     });
3287 };
3288
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3290     
3291     href: false,
3292     target: false,
3293     preventDefault: false,
3294     anchor : false,
3295     alt : false,
3296     fa: false,
3297
3298
3299     getAutoCreate : function()
3300     {
3301         var html = this.html || '';
3302         
3303         if (this.fa !== false) {
3304             html = '<i class="fa fa-' + this.fa + '"></i>';
3305         }
3306         var cfg = {
3307             tag: 'a'
3308         };
3309         // anchor's do not require html/href...
3310         if (this.anchor === false) {
3311             cfg.html = html;
3312             cfg.href = this.href || '#';
3313         } else {
3314             cfg.name = this.anchor;
3315             if (this.html !== false || this.fa !== false) {
3316                 cfg.html = html;
3317             }
3318             if (this.href !== false) {
3319                 cfg.href = this.href;
3320             }
3321         }
3322         
3323         if(this.alt !== false){
3324             cfg.alt = this.alt;
3325         }
3326         
3327         
3328         if(this.target !== false) {
3329             cfg.target = this.target;
3330         }
3331         
3332         return cfg;
3333     },
3334     
3335     initEvents: function() {
3336         
3337         if(!this.href || this.preventDefault){
3338             this.el.on('click', this.onClick, this);
3339         }
3340     },
3341     
3342     onClick : function(e)
3343     {
3344         if(this.preventDefault){
3345             e.preventDefault();
3346         }
3347         //Roo.log('img onclick');
3348         this.fireEvent('click', this, e);
3349     }
3350    
3351 });
3352
3353  /*
3354  * - LGPL
3355  *
3356  * header
3357  * 
3358  */
3359
3360 /**
3361  * @class Roo.bootstrap.Header
3362  * @extends Roo.bootstrap.Component
3363  * Bootstrap Header class
3364  * @cfg {String} html content of header
3365  * @cfg {Number} level (1|2|3|4|5|6) default 1
3366  * 
3367  * @constructor
3368  * Create a new Header
3369  * @param {Object} config The config object
3370  */
3371
3372
3373 Roo.bootstrap.Header  = function(config){
3374     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3375 };
3376
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3378     
3379     //href : false,
3380     html : false,
3381     level : 1,
3382     
3383     
3384     
3385     getAutoCreate : function(){
3386         
3387         
3388         
3389         var cfg = {
3390             tag: 'h' + (1 *this.level),
3391             html: this.html || ''
3392         } ;
3393         
3394         return cfg;
3395     }
3396    
3397 });
3398
3399  
3400
3401  /*
3402  * Based on:
3403  * Ext JS Library 1.1.1
3404  * Copyright(c) 2006-2007, Ext JS, LLC.
3405  *
3406  * Originally Released Under LGPL - original licence link has changed is not relivant.
3407  *
3408  * Fork - LGPL
3409  * <script type="text/javascript">
3410  */
3411  
3412 /**
3413  * @class Roo.bootstrap.MenuMgr
3414  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3415  * @singleton
3416  */
3417 Roo.bootstrap.MenuMgr = function(){
3418    var menus, active, groups = {}, attached = false, lastShow = new Date();
3419
3420    // private - called when first menu is created
3421    function init(){
3422        menus = {};
3423        active = new Roo.util.MixedCollection();
3424        Roo.get(document).addKeyListener(27, function(){
3425            if(active.length > 0){
3426                hideAll();
3427            }
3428        });
3429    }
3430
3431    // private
3432    function hideAll(){
3433        if(active && active.length > 0){
3434            var c = active.clone();
3435            c.each(function(m){
3436                m.hide();
3437            });
3438        }
3439    }
3440
3441    // private
3442    function onHide(m){
3443        active.remove(m);
3444        if(active.length < 1){
3445            Roo.get(document).un("mouseup", onMouseDown);
3446             
3447            attached = false;
3448        }
3449    }
3450
3451    // private
3452    function onShow(m){
3453        var last = active.last();
3454        lastShow = new Date();
3455        active.add(m);
3456        if(!attached){
3457           Roo.get(document).on("mouseup", onMouseDown);
3458            
3459            attached = true;
3460        }
3461        if(m.parentMenu){
3462           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463           m.parentMenu.activeChild = m;
3464        }else if(last && last.isVisible()){
3465           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3466        }
3467    }
3468
3469    // private
3470    function onBeforeHide(m){
3471        if(m.activeChild){
3472            m.activeChild.hide();
3473        }
3474        if(m.autoHideTimer){
3475            clearTimeout(m.autoHideTimer);
3476            delete m.autoHideTimer;
3477        }
3478    }
3479
3480    // private
3481    function onBeforeShow(m){
3482        var pm = m.parentMenu;
3483        if(!pm && !m.allowOtherMenus){
3484            hideAll();
3485        }else if(pm && pm.activeChild && active != m){
3486            pm.activeChild.hide();
3487        }
3488    }
3489
3490    // private this should really trigger on mouseup..
3491    function onMouseDown(e){
3492         Roo.log("on Mouse Up");
3493         
3494         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495             Roo.log("MenuManager hideAll");
3496             hideAll();
3497             e.stopEvent();
3498         }
3499         
3500         
3501    }
3502
3503    // private
3504    function onBeforeCheck(mi, state){
3505        if(state){
3506            var g = groups[mi.group];
3507            for(var i = 0, l = g.length; i < l; i++){
3508                if(g[i] != mi){
3509                    g[i].setChecked(false);
3510                }
3511            }
3512        }
3513    }
3514
3515    return {
3516
3517        /**
3518         * Hides all menus that are currently visible
3519         */
3520        hideAll : function(){
3521             hideAll();  
3522        },
3523
3524        // private
3525        register : function(menu){
3526            if(!menus){
3527                init();
3528            }
3529            menus[menu.id] = menu;
3530            menu.on("beforehide", onBeforeHide);
3531            menu.on("hide", onHide);
3532            menu.on("beforeshow", onBeforeShow);
3533            menu.on("show", onShow);
3534            var g = menu.group;
3535            if(g && menu.events["checkchange"]){
3536                if(!groups[g]){
3537                    groups[g] = [];
3538                }
3539                groups[g].push(menu);
3540                menu.on("checkchange", onCheck);
3541            }
3542        },
3543
3544         /**
3545          * Returns a {@link Roo.menu.Menu} object
3546          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547          * be used to generate and return a new Menu instance.
3548          */
3549        get : function(menu){
3550            if(typeof menu == "string"){ // menu id
3551                return menus[menu];
3552            }else if(menu.events){  // menu instance
3553                return menu;
3554            }
3555            /*else if(typeof menu.length == 'number'){ // array of menu items?
3556                return new Roo.bootstrap.Menu({items:menu});
3557            }else{ // otherwise, must be a config
3558                return new Roo.bootstrap.Menu(menu);
3559            }
3560            */
3561            return false;
3562        },
3563
3564        // private
3565        unregister : function(menu){
3566            delete menus[menu.id];
3567            menu.un("beforehide", onBeforeHide);
3568            menu.un("hide", onHide);
3569            menu.un("beforeshow", onBeforeShow);
3570            menu.un("show", onShow);
3571            var g = menu.group;
3572            if(g && menu.events["checkchange"]){
3573                groups[g].remove(menu);
3574                menu.un("checkchange", onCheck);
3575            }
3576        },
3577
3578        // private
3579        registerCheckable : function(menuItem){
3580            var g = menuItem.group;
3581            if(g){
3582                if(!groups[g]){
3583                    groups[g] = [];
3584                }
3585                groups[g].push(menuItem);
3586                menuItem.on("beforecheckchange", onBeforeCheck);
3587            }
3588        },
3589
3590        // private
3591        unregisterCheckable : function(menuItem){
3592            var g = menuItem.group;
3593            if(g){
3594                groups[g].remove(menuItem);
3595                menuItem.un("beforecheckchange", onBeforeCheck);
3596            }
3597        }
3598    };
3599 }();/*
3600  * - LGPL
3601  *
3602  * menu
3603  * 
3604  */
3605
3606 /**
3607  * @class Roo.bootstrap.Menu
3608  * @extends Roo.bootstrap.Component
3609  * Bootstrap Menu class - container for MenuItems
3610  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3612  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3613  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3614   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3615   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3616  
3617  * @constructor
3618  * Create a new Menu
3619  * @param {Object} config The config object
3620  */
3621
3622
3623 Roo.bootstrap.Menu = function(config){
3624     
3625     if (config.type == 'treeview') {
3626         // normally menu's are drawn attached to the document to handle layering etc..
3627         // however treeview (used by the docs menu is drawn into the parent element)
3628         this.container_method = 'getChildContainer'; 
3629     }
3630     
3631     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632     if (this.registerMenu && this.type != 'treeview')  {
3633         Roo.bootstrap.MenuMgr.register(this);
3634     }
3635     
3636     
3637     this.addEvents({
3638         /**
3639          * @event beforeshow
3640          * Fires before this menu is displayed (return false to block)
3641          * @param {Roo.menu.Menu} this
3642          */
3643         beforeshow : true,
3644         /**
3645          * @event beforehide
3646          * Fires before this menu is hidden (return false to block)
3647          * @param {Roo.menu.Menu} this
3648          */
3649         beforehide : true,
3650         /**
3651          * @event show
3652          * Fires after this menu is displayed
3653          * @param {Roo.menu.Menu} this
3654          */
3655         show : true,
3656         /**
3657          * @event hide
3658          * Fires after this menu is hidden
3659          * @param {Roo.menu.Menu} this
3660          */
3661         hide : true,
3662         /**
3663          * @event click
3664          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665          * @param {Roo.menu.Menu} this
3666          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667          * @param {Roo.EventObject} e
3668          */
3669         click : true,
3670         /**
3671          * @event mouseover
3672          * Fires when the mouse is hovering over this menu
3673          * @param {Roo.menu.Menu} this
3674          * @param {Roo.EventObject} e
3675          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3676          */
3677         mouseover : true,
3678         /**
3679          * @event mouseout
3680          * Fires when the mouse exits this menu
3681          * @param {Roo.menu.Menu} this
3682          * @param {Roo.EventObject} e
3683          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3684          */
3685         mouseout : true,
3686         /**
3687          * @event itemclick
3688          * Fires when a menu item contained in this menu is clicked
3689          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690          * @param {Roo.EventObject} e
3691          */
3692         itemclick: true
3693     });
3694     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3695 };
3696
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3698     
3699    /// html : false,
3700    
3701     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3702     type: false,
3703     /**
3704      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3705      */
3706     registerMenu : true,
3707     
3708     menuItems :false, // stores the menu items..
3709     
3710     hidden:true,
3711         
3712     parentMenu : false,
3713     
3714     stopEvent : true,
3715     
3716     isLink : false,
3717     
3718     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3719     
3720     hideTrigger : false,
3721     
3722     align : 'tl-bl?',
3723     
3724     
3725     getChildContainer : function() {
3726         return this.el;  
3727     },
3728     
3729     getAutoCreate : function(){
3730          
3731         //if (['right'].indexOf(this.align)!==-1) {
3732         //    cfg.cn[1].cls += ' pull-right'
3733         //}
3734          
3735         var cfg = {
3736             tag : 'ul',
3737             cls : 'dropdown-menu shadow' ,
3738             style : 'z-index:1000'
3739             
3740         };
3741         
3742         if (this.type === 'submenu') {
3743             cfg.cls = 'submenu active';
3744         }
3745         if (this.type === 'treeview') {
3746             cfg.cls = 'treeview-menu';
3747         }
3748         
3749         return cfg;
3750     },
3751     initEvents : function() {
3752         
3753        // Roo.log("ADD event");
3754        // Roo.log(this.triggerEl.dom);
3755         if (this.triggerEl) {
3756             
3757             this.triggerEl.on('click', this.onTriggerClick, this);
3758             
3759             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3760             
3761             if (!this.hideTrigger) {
3762                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763                     // dropdown toggle on the 'a' in BS4?
3764                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3765                 } else {
3766                     this.triggerEl.addClass('dropdown-toggle');
3767                 }
3768             }
3769         }
3770         
3771         if (Roo.isTouch) {
3772             this.el.on('touchstart'  , this.onTouch, this);
3773         }
3774         this.el.on('click' , this.onClick, this);
3775
3776         this.el.on("mouseover", this.onMouseOver, this);
3777         this.el.on("mouseout", this.onMouseOut, this);
3778         
3779     },
3780     
3781     findTargetItem : function(e)
3782     {
3783         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3784         if(!t){
3785             return false;
3786         }
3787         //Roo.log(t);         Roo.log(t.id);
3788         if(t && t.id){
3789             //Roo.log(this.menuitems);
3790             return this.menuitems.get(t.id);
3791             
3792             //return this.items.get(t.menuItemId);
3793         }
3794         
3795         return false;
3796     },
3797     
3798     onTouch : function(e) 
3799     {
3800         Roo.log("menu.onTouch");
3801         //e.stopEvent(); this make the user popdown broken
3802         this.onClick(e);
3803     },
3804     
3805     onClick : function(e)
3806     {
3807         Roo.log("menu.onClick");
3808         
3809         var t = this.findTargetItem(e);
3810         if(!t || t.isContainer){
3811             return;
3812         }
3813         Roo.log(e);
3814         /*
3815         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3816             if(t == this.activeItem && t.shouldDeactivate(e)){
3817                 this.activeItem.deactivate();
3818                 delete this.activeItem;
3819                 return;
3820             }
3821             if(t.canActivate){
3822                 this.setActiveItem(t, true);
3823             }
3824             return;
3825             
3826             
3827         }
3828         */
3829        
3830         Roo.log('pass click event');
3831         
3832         t.onClick(e);
3833         
3834         this.fireEvent("click", this, t, e);
3835         
3836         var _this = this;
3837         
3838         if(!t.href.length || t.href == '#'){
3839             (function() { _this.hide(); }).defer(100);
3840         }
3841         
3842     },
3843     
3844     onMouseOver : function(e){
3845         var t  = this.findTargetItem(e);
3846         //Roo.log(t);
3847         //if(t){
3848         //    if(t.canActivate && !t.disabled){
3849         //        this.setActiveItem(t, true);
3850         //    }
3851         //}
3852         
3853         this.fireEvent("mouseover", this, e, t);
3854     },
3855     isVisible : function(){
3856         return !this.hidden;
3857     },
3858     onMouseOut : function(e){
3859         var t  = this.findTargetItem(e);
3860         
3861         //if(t ){
3862         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3863         //        this.activeItem.deactivate();
3864         //        delete this.activeItem;
3865         //    }
3866         //}
3867         this.fireEvent("mouseout", this, e, t);
3868     },
3869     
3870     
3871     /**
3872      * Displays this menu relative to another element
3873      * @param {String/HTMLElement/Roo.Element} element The element to align to
3874      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875      * the element (defaults to this.defaultAlign)
3876      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3877      */
3878     show : function(el, pos, parentMenu)
3879     {
3880         if (false === this.fireEvent("beforeshow", this)) {
3881             Roo.log("show canceled");
3882             return;
3883         }
3884         this.parentMenu = parentMenu;
3885         if(!this.el){
3886             this.render();
3887         }
3888         this.el.addClass('show'); // show otherwise we do not know how big we are..
3889          
3890         var xy = this.el.getAlignToXY(el, pos);
3891         
3892         // bl-tl << left align  below
3893         // tl-bl << left align 
3894         
3895         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896             // if it goes to far to the right.. -> align left.
3897             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3898         }
3899         if(xy[0] < 0){
3900             // was left align - go right?
3901             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3902         }
3903         
3904         // goes down the bottom
3905         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3906            xy[1]  < 0 ){
3907             var a = this.align.replace('?', '').split('-');
3908             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3909             
3910         }
3911         
3912         this.showAt(  xy , parentMenu, false);
3913     },
3914      /**
3915      * Displays this menu at a specific xy position
3916      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3918      */
3919     showAt : function(xy, parentMenu, /* private: */_e){
3920         this.parentMenu = parentMenu;
3921         if(!this.el){
3922             this.render();
3923         }
3924         if(_e !== false){
3925             this.fireEvent("beforeshow", this);
3926             //xy = this.el.adjustForConstraints(xy);
3927         }
3928         
3929         //this.el.show();
3930         this.hideMenuItems();
3931         this.hidden = false;
3932         if (this.triggerEl) {
3933             this.triggerEl.addClass('open');
3934         }
3935         
3936         this.el.addClass('show');
3937         
3938         
3939         
3940         // reassign x when hitting right
3941         
3942         // reassign y when hitting bottom
3943         
3944         // but the list may align on trigger left or trigger top... should it be a properity?
3945         
3946         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3947             this.el.setXY(xy);
3948         }
3949         
3950         this.focus();
3951         this.fireEvent("show", this);
3952     },
3953     
3954     focus : function(){
3955         return;
3956         if(!this.hidden){
3957             this.doFocus.defer(50, this);
3958         }
3959     },
3960
3961     doFocus : function(){
3962         if(!this.hidden){
3963             this.focusEl.focus();
3964         }
3965     },
3966
3967     /**
3968      * Hides this menu and optionally all parent menus
3969      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3970      */
3971     hide : function(deep)
3972     {
3973         if (false === this.fireEvent("beforehide", this)) {
3974             Roo.log("hide canceled");
3975             return;
3976         }
3977         this.hideMenuItems();
3978         if(this.el && this.isVisible()){
3979            
3980             if(this.activeItem){
3981                 this.activeItem.deactivate();
3982                 this.activeItem = null;
3983             }
3984             if (this.triggerEl) {
3985                 this.triggerEl.removeClass('open');
3986             }
3987             
3988             this.el.removeClass('show');
3989             this.hidden = true;
3990             this.fireEvent("hide", this);
3991         }
3992         if(deep === true && this.parentMenu){
3993             this.parentMenu.hide(true);
3994         }
3995     },
3996     
3997     onTriggerClick : function(e)
3998     {
3999         Roo.log('trigger click');
4000         
4001         var target = e.getTarget();
4002         
4003         Roo.log(target.nodeName.toLowerCase());
4004         
4005         if(target.nodeName.toLowerCase() === 'i'){
4006             e.preventDefault();
4007         }
4008         
4009     },
4010     
4011     onTriggerPress  : function(e)
4012     {
4013         Roo.log('trigger press');
4014         //Roo.log(e.getTarget());
4015        // Roo.log(this.triggerEl.dom);
4016        
4017         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018         var pel = Roo.get(e.getTarget());
4019         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020             Roo.log('is treeview or dropdown?');
4021             return;
4022         }
4023         
4024         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4025             return;
4026         }
4027         
4028         if (this.isVisible()) {
4029             Roo.log('hide');
4030             this.hide();
4031         } else {
4032             Roo.log('show');
4033             
4034             this.show(this.triggerEl, this.align, false);
4035         }
4036         
4037         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4038             e.stopEvent();
4039         }
4040         
4041     },
4042        
4043     
4044     hideMenuItems : function()
4045     {
4046         Roo.log("hide Menu Items");
4047         if (!this.el) { 
4048             return;
4049         }
4050         
4051         this.el.select('.open',true).each(function(aa) {
4052             
4053             aa.removeClass('open');
4054          
4055         });
4056     },
4057     addxtypeChild : function (tree, cntr) {
4058         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4059           
4060         this.menuitems.add(comp);
4061         return comp;
4062
4063     },
4064     getEl : function()
4065     {
4066         Roo.log(this.el);
4067         return this.el;
4068     },
4069     
4070     clear : function()
4071     {
4072         this.getEl().dom.innerHTML = '';
4073         this.menuitems.clear();
4074     }
4075 });
4076
4077  
4078  /*
4079  * - LGPL
4080  *
4081  * menu item
4082  * 
4083  */
4084
4085
4086 /**
4087  * @class Roo.bootstrap.MenuItem
4088  * @extends Roo.bootstrap.Component
4089  * Bootstrap MenuItem class
4090  * @cfg {String} html the menu label
4091  * @cfg {String} href the link
4092  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4095  * @cfg {String} fa favicon to show on left of menu item.
4096  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4097  * 
4098  * 
4099  * @constructor
4100  * Create a new MenuItem
4101  * @param {Object} config The config object
4102  */
4103
4104
4105 Roo.bootstrap.MenuItem = function(config){
4106     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4107     this.addEvents({
4108         // raw events
4109         /**
4110          * @event click
4111          * The raw click event for the entire grid.
4112          * @param {Roo.bootstrap.MenuItem} this
4113          * @param {Roo.EventObject} e
4114          */
4115         "click" : true
4116     });
4117 };
4118
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4120     
4121     href : false,
4122     html : false,
4123     preventDefault: false,
4124     isContainer : false,
4125     active : false,
4126     fa: false,
4127     
4128     getAutoCreate : function(){
4129         
4130         if(this.isContainer){
4131             return {
4132                 tag: 'li',
4133                 cls: 'dropdown-menu-item '
4134             };
4135         }
4136         var ctag = {
4137             tag: 'span',
4138             html: 'Link'
4139         };
4140         
4141         var anc = {
4142             tag : 'a',
4143             cls : 'dropdown-item',
4144             href : '#',
4145             cn : [  ]
4146         };
4147         
4148         if (this.fa !== false) {
4149             anc.cn.push({
4150                 tag : 'i',
4151                 cls : 'fa fa-' + this.fa
4152             });
4153         }
4154         
4155         anc.cn.push(ctag);
4156         
4157         
4158         var cfg= {
4159             tag: 'li',
4160             cls: 'dropdown-menu-item',
4161             cn: [ anc ]
4162         };
4163         if (this.parent().type == 'treeview') {
4164             cfg.cls = 'treeview-menu';
4165         }
4166         if (this.active) {
4167             cfg.cls += ' active';
4168         }
4169         
4170         
4171         
4172         anc.href = this.href || cfg.cn[0].href ;
4173         ctag.html = this.html || cfg.cn[0].html ;
4174         return cfg;
4175     },
4176     
4177     initEvents: function()
4178     {
4179         if (this.parent().type == 'treeview') {
4180             this.el.select('a').on('click', this.onClick, this);
4181         }
4182         
4183         if (this.menu) {
4184             this.menu.parentType = this.xtype;
4185             this.menu.triggerEl = this.el;
4186             this.menu = this.addxtype(Roo.apply({}, this.menu));
4187         }
4188         
4189     },
4190     onClick : function(e)
4191     {
4192         Roo.log('item on click ');
4193         
4194         if(this.preventDefault){
4195             e.preventDefault();
4196         }
4197         //this.parent().hideMenuItems();
4198         
4199         this.fireEvent('click', this, e);
4200     },
4201     getEl : function()
4202     {
4203         return this.el;
4204     } 
4205 });
4206
4207  
4208
4209  /*
4210  * - LGPL
4211  *
4212  * menu separator
4213  * 
4214  */
4215
4216
4217 /**
4218  * @class Roo.bootstrap.MenuSeparator
4219  * @extends Roo.bootstrap.Component
4220  * Bootstrap MenuSeparator class
4221  * 
4222  * @constructor
4223  * Create a new MenuItem
4224  * @param {Object} config The config object
4225  */
4226
4227
4228 Roo.bootstrap.MenuSeparator = function(config){
4229     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4230 };
4231
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4233     
4234     getAutoCreate : function(){
4235         var cfg = {
4236             cls: 'divider',
4237             tag : 'li'
4238         };
4239         
4240         return cfg;
4241     }
4242    
4243 });
4244
4245  
4246
4247  
4248 /*
4249 * Licence: LGPL
4250 */
4251
4252 /**
4253  * @class Roo.bootstrap.Modal
4254  * @extends Roo.bootstrap.Component
4255  * Bootstrap Modal class
4256  * @cfg {String} title Title of dialog
4257  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4259  * @cfg {Boolean} specificTitle default false
4260  * @cfg {Array} buttons Array of buttons or standard button set..
4261  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262  * @cfg {Boolean} animate default true
4263  * @cfg {Boolean} allow_close default true
4264  * @cfg {Boolean} fitwindow default false
4265  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268  * @cfg {String} size (sm|lg|xl) default empty
4269  * @cfg {Number} max_width set the max width of modal
4270  * @cfg {Boolean} editableTitle can the title be edited
4271
4272  *
4273  *
4274  * @constructor
4275  * Create a new Modal Dialog
4276  * @param {Object} config The config object
4277  */
4278
4279 Roo.bootstrap.Modal = function(config){
4280     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4281     this.addEvents({
4282         // raw events
4283         /**
4284          * @event btnclick
4285          * The raw btnclick event for the button
4286          * @param {Roo.EventObject} e
4287          */
4288         "btnclick" : true,
4289         /**
4290          * @event resize
4291          * Fire when dialog resize
4292          * @param {Roo.bootstrap.Modal} this
4293          * @param {Roo.EventObject} e
4294          */
4295         "resize" : true,
4296         /**
4297          * @event titlechanged
4298          * Fire when the editable title has been changed
4299          * @param {Roo.bootstrap.Modal} this
4300          * @param {Roo.EventObject} value
4301          */
4302         "titlechanged" : true 
4303         
4304     });
4305     this.buttons = this.buttons || [];
4306
4307     if (this.tmpl) {
4308         this.tmpl = Roo.factory(this.tmpl);
4309     }
4310
4311 };
4312
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4314
4315     title : 'test dialog',
4316
4317     buttons : false,
4318
4319     // set on load...
4320
4321     html: false,
4322
4323     tmp: false,
4324
4325     specificTitle: false,
4326
4327     buttonPosition: 'right',
4328
4329     allow_close : true,
4330
4331     animate : true,
4332
4333     fitwindow: false,
4334     
4335      // private
4336     dialogEl: false,
4337     bodyEl:  false,
4338     footerEl:  false,
4339     titleEl:  false,
4340     closeEl:  false,
4341
4342     size: '',
4343     
4344     max_width: 0,
4345     
4346     max_height: 0,
4347     
4348     fit_content: false,
4349     editableTitle  : false,
4350
4351     onRender : function(ct, position)
4352     {
4353         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4354
4355         if(!this.el){
4356             var cfg = Roo.apply({},  this.getAutoCreate());
4357             cfg.id = Roo.id();
4358             //if(!cfg.name){
4359             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4360             //}
4361             //if (!cfg.name.length) {
4362             //    delete cfg.name;
4363            // }
4364             if (this.cls) {
4365                 cfg.cls += ' ' + this.cls;
4366             }
4367             if (this.style) {
4368                 cfg.style = this.style;
4369             }
4370             this.el = Roo.get(document.body).createChild(cfg, position);
4371         }
4372         //var type = this.el.dom.type;
4373
4374
4375         if(this.tabIndex !== undefined){
4376             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4377         }
4378
4379         this.dialogEl = this.el.select('.modal-dialog',true).first();
4380         this.bodyEl = this.el.select('.modal-body',true).first();
4381         this.closeEl = this.el.select('.modal-header .close', true).first();
4382         this.headerEl = this.el.select('.modal-header',true).first();
4383         this.titleEl = this.el.select('.modal-title',true).first();
4384         this.footerEl = this.el.select('.modal-footer',true).first();
4385
4386         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4387         
4388         //this.el.addClass("x-dlg-modal");
4389
4390         if (this.buttons.length) {
4391             Roo.each(this.buttons, function(bb) {
4392                 var b = Roo.apply({}, bb);
4393                 b.xns = b.xns || Roo.bootstrap;
4394                 b.xtype = b.xtype || 'Button';
4395                 if (typeof(b.listeners) == 'undefined') {
4396                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4397                 }
4398
4399                 var btn = Roo.factory(b);
4400
4401                 btn.render(this.getButtonContainer());
4402
4403             },this);
4404         }
4405         // render the children.
4406         var nitems = [];
4407
4408         if(typeof(this.items) != 'undefined'){
4409             var items = this.items;
4410             delete this.items;
4411
4412             for(var i =0;i < items.length;i++) {
4413                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4414             }
4415         }
4416
4417         this.items = nitems;
4418
4419         // where are these used - they used to be body/close/footer
4420
4421
4422         this.initEvents();
4423         //this.el.addClass([this.fieldClass, this.cls]);
4424
4425     },
4426
4427     getAutoCreate : function()
4428     {
4429         // we will default to modal-body-overflow - might need to remove or make optional later.
4430         var bdy = {
4431                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4432                 html : this.html || ''
4433         };
4434
4435         var title = {
4436             tag: 'h5',
4437             cls : 'modal-title',
4438             html : this.title
4439         };
4440
4441         if(this.specificTitle){ // WTF is this?
4442             title = this.title;
4443         }
4444
4445         var header = [];
4446         if (this.allow_close && Roo.bootstrap.version == 3) {
4447             header.push({
4448                 tag: 'button',
4449                 cls : 'close',
4450                 html : '&times'
4451             });
4452         }
4453
4454         header.push(title);
4455
4456         if (this.editableTitle) {
4457             header.push({
4458                 cls: 'form-control roo-editable-title d-none',
4459                 tag: 'input',
4460                 type: 'text'
4461             });
4462         }
4463         
4464         if (this.allow_close && Roo.bootstrap.version == 4) {
4465             header.push({
4466                 tag: 'button',
4467                 cls : 'close',
4468                 html : '&times'
4469             });
4470         }
4471         
4472         var size = '';
4473
4474         if(this.size.length){
4475             size = 'modal-' + this.size;
4476         }
4477         
4478         var footer = Roo.bootstrap.version == 3 ?
4479             {
4480                 cls : 'modal-footer',
4481                 cn : [
4482                     {
4483                         tag: 'div',
4484                         cls: 'btn-' + this.buttonPosition
4485                     }
4486                 ]
4487
4488             } :
4489             {  // BS4 uses mr-auto on left buttons....
4490                 cls : 'modal-footer'
4491             };
4492
4493             
4494
4495         
4496         
4497         var modal = {
4498             cls: "modal",
4499              cn : [
4500                 {
4501                     cls: "modal-dialog " + size,
4502                     cn : [
4503                         {
4504                             cls : "modal-content",
4505                             cn : [
4506                                 {
4507                                     cls : 'modal-header',
4508                                     cn : header
4509                                 },
4510                                 bdy,
4511                                 footer
4512                             ]
4513
4514                         }
4515                     ]
4516
4517                 }
4518             ]
4519         };
4520
4521         if(this.animate){
4522             modal.cls += ' fade';
4523         }
4524
4525         return modal;
4526
4527     },
4528     getChildContainer : function() {
4529
4530          return this.bodyEl;
4531
4532     },
4533     getButtonContainer : function() {
4534         
4535          return Roo.bootstrap.version == 4 ?
4536             this.el.select('.modal-footer',true).first()
4537             : this.el.select('.modal-footer div',true).first();
4538
4539     },
4540     initEvents : function()
4541     {
4542         if (this.allow_close) {
4543             this.closeEl.on('click', this.hide, this);
4544         }
4545         Roo.EventManager.onWindowResize(this.resize, this, true);
4546         if (this.editableTitle) {
4547             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4548             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549             this.headerEditEl.on('keyup', function(e) {
4550                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551                         this.toggleHeaderInput(false)
4552                     }
4553                 }, this);
4554             this.headerEditEl.on('blur', function(e) {
4555                 this.toggleHeaderInput(false)
4556             },this);
4557         }
4558
4559     },
4560   
4561
4562     resize : function()
4563     {
4564         this.maskEl.setSize(
4565             Roo.lib.Dom.getViewWidth(true),
4566             Roo.lib.Dom.getViewHeight(true)
4567         );
4568         
4569         if (this.fitwindow) {
4570             
4571            this.dialogEl.setStyle( { 'max-width' : '100%' });
4572             this.setSize(
4573                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4575             );
4576             return;
4577         }
4578         
4579         if(this.max_width !== 0) {
4580             
4581             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4582             
4583             if(this.height) {
4584                 this.setSize(w, this.height);
4585                 return;
4586             }
4587             
4588             if(this.max_height) {
4589                 this.setSize(w,Math.min(
4590                     this.max_height,
4591                     Roo.lib.Dom.getViewportHeight(true) - 60
4592                 ));
4593                 
4594                 return;
4595             }
4596             
4597             if(!this.fit_content) {
4598                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4599                 return;
4600             }
4601             
4602             this.setSize(w, Math.min(
4603                 60 +
4604                 this.headerEl.getHeight() + 
4605                 this.footerEl.getHeight() + 
4606                 this.getChildHeight(this.bodyEl.dom.childNodes),
4607                 Roo.lib.Dom.getViewportHeight(true) - 60)
4608             );
4609         }
4610         
4611     },
4612
4613     setSize : function(w,h)
4614     {
4615         if (!w && !h) {
4616             return;
4617         }
4618         
4619         this.resizeTo(w,h);
4620     },
4621
4622     show : function() {
4623
4624         if (!this.rendered) {
4625             this.render();
4626         }
4627         this.toggleHeaderInput(false);
4628         //this.el.setStyle('display', 'block');
4629         this.el.removeClass('hideing');
4630         this.el.dom.style.display='block';
4631         
4632         Roo.get(document.body).addClass('modal-open');
4633  
4634         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4635             
4636             (function(){
4637                 this.el.addClass('show');
4638                 this.el.addClass('in');
4639             }).defer(50, this);
4640         }else{
4641             this.el.addClass('show');
4642             this.el.addClass('in');
4643         }
4644
4645         // not sure how we can show data in here..
4646         //if (this.tmpl) {
4647         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4648         //}
4649
4650         Roo.get(document.body).addClass("x-body-masked");
4651         
4652         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4653         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654         this.maskEl.dom.style.display = 'block';
4655         this.maskEl.addClass('show');
4656         
4657         
4658         this.resize();
4659         
4660         this.fireEvent('show', this);
4661
4662         // set zindex here - otherwise it appears to be ignored...
4663         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664
4665         (function () {
4666             this.items.forEach( function(e) {
4667                 e.layout ? e.layout() : false;
4668
4669             });
4670         }).defer(100,this);
4671
4672     },
4673     hide : function()
4674     {
4675         if(this.fireEvent("beforehide", this) !== false){
4676             
4677             this.maskEl.removeClass('show');
4678             
4679             this.maskEl.dom.style.display = '';
4680             Roo.get(document.body).removeClass("x-body-masked");
4681             this.el.removeClass('in');
4682             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4683
4684             if(this.animate){ // why
4685                 this.el.addClass('hideing');
4686                 this.el.removeClass('show');
4687                 (function(){
4688                     if (!this.el.hasClass('hideing')) {
4689                         return; // it's been shown again...
4690                     }
4691                     
4692                     this.el.dom.style.display='';
4693
4694                     Roo.get(document.body).removeClass('modal-open');
4695                     this.el.removeClass('hideing');
4696                 }).defer(150,this);
4697                 
4698             }else{
4699                 this.el.removeClass('show');
4700                 this.el.dom.style.display='';
4701                 Roo.get(document.body).removeClass('modal-open');
4702
4703             }
4704             this.fireEvent('hide', this);
4705         }
4706     },
4707     isVisible : function()
4708     {
4709         
4710         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4711         
4712     },
4713
4714     addButton : function(str, cb)
4715     {
4716
4717
4718         var b = Roo.apply({}, { html : str } );
4719         b.xns = b.xns || Roo.bootstrap;
4720         b.xtype = b.xtype || 'Button';
4721         if (typeof(b.listeners) == 'undefined') {
4722             b.listeners = { click : cb.createDelegate(this)  };
4723         }
4724
4725         var btn = Roo.factory(b);
4726
4727         btn.render(this.getButtonContainer());
4728
4729         return btn;
4730
4731     },
4732
4733     setDefaultButton : function(btn)
4734     {
4735         //this.el.select('.modal-footer').()
4736     },
4737
4738     resizeTo: function(w,h)
4739     {
4740         this.dialogEl.setWidth(w);
4741         
4742         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4743
4744         this.bodyEl.setHeight(h - diff);
4745         
4746         this.fireEvent('resize', this);
4747     },
4748     
4749     setContentSize  : function(w, h)
4750     {
4751
4752     },
4753     onButtonClick: function(btn,e)
4754     {
4755         //Roo.log([a,b,c]);
4756         this.fireEvent('btnclick', btn.name, e);
4757     },
4758      /**
4759      * Set the title of the Dialog
4760      * @param {String} str new Title
4761      */
4762     setTitle: function(str) {
4763         this.titleEl.dom.innerHTML = str;
4764         this.title = str;
4765     },
4766     /**
4767      * Set the body of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setBody: function(str) {
4771         this.bodyEl.dom.innerHTML = str;
4772     },
4773     /**
4774      * Set the body of the Dialog using the template
4775      * @param {Obj} data - apply this data to the template and replace the body contents.
4776      */
4777     applyBody: function(obj)
4778     {
4779         if (!this.tmpl) {
4780             Roo.log("Error - using apply Body without a template");
4781             //code
4782         }
4783         this.tmpl.overwrite(this.bodyEl, obj);
4784     },
4785     
4786     getChildHeight : function(child_nodes)
4787     {
4788         if(
4789             !child_nodes ||
4790             child_nodes.length == 0
4791         ) {
4792             return 0;
4793         }
4794         
4795         var child_height = 0;
4796         
4797         for(var i = 0; i < child_nodes.length; i++) {
4798             
4799             /*
4800             * for modal with tabs...
4801             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4802                 
4803                 var layout_childs = child_nodes[i].childNodes;
4804                 
4805                 for(var j = 0; j < layout_childs.length; j++) {
4806                     
4807                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4808                         
4809                         var layout_body_childs = layout_childs[j].childNodes;
4810                         
4811                         for(var k = 0; k < layout_body_childs.length; k++) {
4812                             
4813                             if(layout_body_childs[k].classList.contains('navbar')) {
4814                                 child_height += layout_body_childs[k].offsetHeight;
4815                                 continue;
4816                             }
4817                             
4818                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4819                                 
4820                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4821                                 
4822                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4823                                     
4824                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4826                                         continue;
4827                                     }
4828                                     
4829                                 }
4830                                 
4831                             }
4832                             
4833                         }
4834                     }
4835                 }
4836                 continue;
4837             }
4838             */
4839             
4840             child_height += child_nodes[i].offsetHeight;
4841             // Roo.log(child_nodes[i].offsetHeight);
4842         }
4843         
4844         return child_height;
4845     },
4846     toggleHeaderInput : function(is_edit)
4847     {
4848         if (!this.editableTitle) {
4849             return; // not editable.
4850         }
4851         if (is_edit && this.is_header_editing) {
4852             return; // already editing..
4853         }
4854         if (is_edit) {
4855     
4856             this.headerEditEl.dom.value = this.title;
4857             this.headerEditEl.removeClass('d-none');
4858             this.headerEditEl.dom.focus();
4859             this.titleEl.addClass('d-none');
4860             
4861             this.is_header_editing = true;
4862             return
4863         }
4864         // flip back to not editing.
4865         this.title = this.headerEditEl.dom.value;
4866         this.headerEditEl.addClass('d-none');
4867         this.titleEl.removeClass('d-none');
4868         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869         this.is_header_editing = false;
4870         this.fireEvent('titlechanged', this, this.title);
4871     
4872             
4873         
4874     }
4875
4876 });
4877
4878
4879 Roo.apply(Roo.bootstrap.Modal,  {
4880     /**
4881          * Button config that displays a single OK button
4882          * @type Object
4883          */
4884         OK :  [{
4885             name : 'ok',
4886             weight : 'primary',
4887             html : 'OK'
4888         }],
4889         /**
4890          * Button config that displays Yes and No buttons
4891          * @type Object
4892          */
4893         YESNO : [
4894             {
4895                 name  : 'no',
4896                 html : 'No'
4897             },
4898             {
4899                 name  :'yes',
4900                 weight : 'primary',
4901                 html : 'Yes'
4902             }
4903         ],
4904
4905         /**
4906          * Button config that displays OK and Cancel buttons
4907          * @type Object
4908          */
4909         OKCANCEL : [
4910             {
4911                name : 'cancel',
4912                 html : 'Cancel'
4913             },
4914             {
4915                 name : 'ok',
4916                 weight : 'primary',
4917                 html : 'OK'
4918             }
4919         ],
4920         /**
4921          * Button config that displays Yes, No and Cancel buttons
4922          * @type Object
4923          */
4924         YESNOCANCEL : [
4925             {
4926                 name : 'yes',
4927                 weight : 'primary',
4928                 html : 'Yes'
4929             },
4930             {
4931                 name : 'no',
4932                 html : 'No'
4933             },
4934             {
4935                 name : 'cancel',
4936                 html : 'Cancel'
4937             }
4938         ],
4939         
4940         zIndex : 10001
4941 });
4942
4943 /*
4944  * - LGPL
4945  *
4946  * messagebox - can be used as a replace
4947  * 
4948  */
4949 /**
4950  * @class Roo.MessageBox
4951  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4952  * Example usage:
4953  *<pre><code>
4954 // Basic alert:
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4956
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4959     if (btn == 'ok'){
4960         // process text value...
4961     }
4962 });
4963
4964 // Show a dialog using config options:
4965 Roo.Msg.show({
4966    title:'Save Changes?',
4967    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968    buttons: Roo.Msg.YESNOCANCEL,
4969    fn: processResult,
4970    animEl: 'elId'
4971 });
4972 </code></pre>
4973  * @singleton
4974  */
4975 Roo.bootstrap.MessageBox = function(){
4976     var dlg, opt, mask, waitTimer;
4977     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978     var buttons, activeTextEl, bwidth;
4979
4980     
4981     // private
4982     var handleButton = function(button){
4983         dlg.hide();
4984         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4985     };
4986
4987     // private
4988     var handleHide = function(){
4989         if(opt && opt.cls){
4990             dlg.el.removeClass(opt.cls);
4991         }
4992         //if(waitTimer){
4993         //    Roo.TaskMgr.stop(waitTimer);
4994         //    waitTimer = null;
4995         //}
4996     };
4997
4998     // private
4999     var updateButtons = function(b){
5000         var width = 0;
5001         if(!b){
5002             buttons["ok"].hide();
5003             buttons["cancel"].hide();
5004             buttons["yes"].hide();
5005             buttons["no"].hide();
5006             dlg.footerEl.hide();
5007             
5008             return width;
5009         }
5010         dlg.footerEl.show();
5011         for(var k in buttons){
5012             if(typeof buttons[k] != "function"){
5013                 if(b[k]){
5014                     buttons[k].show();
5015                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016                     width += buttons[k].el.getWidth()+15;
5017                 }else{
5018                     buttons[k].hide();
5019                 }
5020             }
5021         }
5022         return width;
5023     };
5024
5025     // private
5026     var handleEsc = function(d, k, e){
5027         if(opt && opt.closable !== false){
5028             dlg.hide();
5029         }
5030         if(e){
5031             e.stopEvent();
5032         }
5033     };
5034
5035     return {
5036         /**
5037          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038          * @return {Roo.BasicDialog} The BasicDialog element
5039          */
5040         getDialog : function(){
5041            if(!dlg){
5042                 dlg = new Roo.bootstrap.Modal( {
5043                     //draggable: true,
5044                     //resizable:false,
5045                     //constraintoviewport:false,
5046                     //fixedcenter:true,
5047                     //collapsible : false,
5048                     //shim:true,
5049                     //modal: true,
5050                 //    width: 'auto',
5051                   //  height:100,
5052                     //buttonAlign:"center",
5053                     closeClick : function(){
5054                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5055                             handleButton("no");
5056                         }else{
5057                             handleButton("cancel");
5058                         }
5059                     }
5060                 });
5061                 dlg.render();
5062                 dlg.on("hide", handleHide);
5063                 mask = dlg.mask;
5064                 //dlg.addKeyListener(27, handleEsc);
5065                 buttons = {};
5066                 this.buttons = buttons;
5067                 var bt = this.buttonText;
5068                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5072                 //Roo.log(buttons);
5073                 bodyEl = dlg.bodyEl.createChild({
5074
5075                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076                         '<textarea class="roo-mb-textarea"></textarea>' +
5077                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5078                 });
5079                 msgEl = bodyEl.dom.firstChild;
5080                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081                 textboxEl.enableDisplayMode();
5082                 textboxEl.addKeyListener([10,13], function(){
5083                     if(dlg.isVisible() && opt && opt.buttons){
5084                         if(opt.buttons.ok){
5085                             handleButton("ok");
5086                         }else if(opt.buttons.yes){
5087                             handleButton("yes");
5088                         }
5089                     }
5090                 });
5091                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092                 textareaEl.enableDisplayMode();
5093                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094                 progressEl.enableDisplayMode();
5095                 
5096                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097                 var pf = progressEl.dom.firstChild;
5098                 if (pf) {
5099                     pp = Roo.get(pf.firstChild);
5100                     pp.setHeight(pf.offsetHeight);
5101                 }
5102                 
5103             }
5104             return dlg;
5105         },
5106
5107         /**
5108          * Updates the message box body text
5109          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110          * the XHTML-compliant non-breaking space character '&amp;#160;')
5111          * @return {Roo.MessageBox} This message box
5112          */
5113         updateText : function(text)
5114         {
5115             if(!dlg.isVisible() && !opt.width){
5116                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5118             }
5119             msgEl.innerHTML = text || '&#160;';
5120       
5121             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5123             var w = Math.max(
5124                     Math.min(opt.width || cw , this.maxWidth), 
5125                     Math.max(opt.minWidth || this.minWidth, bwidth)
5126             );
5127             if(opt.prompt){
5128                 activeTextEl.setWidth(w);
5129             }
5130             if(dlg.isVisible()){
5131                 dlg.fixedcenter = false;
5132             }
5133             // to big, make it scroll. = But as usual stupid IE does not support
5134             // !important..
5135             
5136             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5139             } else {
5140                 bodyEl.dom.style.height = '';
5141                 bodyEl.dom.style.overflowY = '';
5142             }
5143             if (cw > w) {
5144                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5145             } else {
5146                 bodyEl.dom.style.overflowX = '';
5147             }
5148             
5149             dlg.setContentSize(w, bodyEl.getHeight());
5150             if(dlg.isVisible()){
5151                 dlg.fixedcenter = true;
5152             }
5153             return this;
5154         },
5155
5156         /**
5157          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5158          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161          * @return {Roo.MessageBox} This message box
5162          */
5163         updateProgress : function(value, text){
5164             if(text){
5165                 this.updateText(text);
5166             }
5167             
5168             if (pp) { // weird bug on my firefox - for some reason this is not defined
5169                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5171             }
5172             return this;
5173         },        
5174
5175         /**
5176          * Returns true if the message box is currently displayed
5177          * @return {Boolean} True if the message box is visible, else false
5178          */
5179         isVisible : function(){
5180             return dlg && dlg.isVisible();  
5181         },
5182
5183         /**
5184          * Hides the message box if it is displayed
5185          */
5186         hide : function(){
5187             if(this.isVisible()){
5188                 dlg.hide();
5189             }  
5190         },
5191
5192         /**
5193          * Displays a new message box, or reinitializes an existing message box, based on the config options
5194          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195          * The following config object properties are supported:
5196          * <pre>
5197 Property    Type             Description
5198 ----------  ---------------  ------------------------------------------------------------------------------------
5199 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5200                                    closes (defaults to undefined)
5201 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5204                                    progress and wait dialogs will ignore this property and always hide the
5205                                    close button as they can only be closed programmatically.
5206 cls               String           A custom CSS class to apply to the message box element
5207 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5208                                    displayed (defaults to 75)
5209 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5210                                    function will be btn (the name of the button that was clicked, if applicable,
5211                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5212                                    Progress and wait dialogs will ignore this option since they do not respond to
5213                                    user actions and can only be closed programmatically, so any required function
5214                                    should be called by the same code after it closes the dialog.
5215 icon              String           A CSS class that provides a background image to be used as an icon for
5216                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5218 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5219 modal             Boolean          False to allow user interaction with the page while the message box is
5220                                    displayed (defaults to true)
5221 msg               String           A string that will replace the existing message box body text (defaults
5222                                    to the XHTML-compliant non-breaking space character '&#160;')
5223 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5224 progress          Boolean          True to display a progress bar (defaults to false)
5225 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5228 title             String           The title text
5229 value             String           The string value to set into the active textbox element if displayed
5230 wait              Boolean          True to display a progress bar (defaults to false)
5231 width             Number           The width of the dialog in pixels
5232 </pre>
5233          *
5234          * Example usage:
5235          * <pre><code>
5236 Roo.Msg.show({
5237    title: 'Address',
5238    msg: 'Please enter your address:',
5239    width: 300,
5240    buttons: Roo.MessageBox.OKCANCEL,
5241    multiline: true,
5242    fn: saveAddress,
5243    animEl: 'addAddressBtn'
5244 });
5245 </code></pre>
5246          * @param {Object} config Configuration options
5247          * @return {Roo.MessageBox} This message box
5248          */
5249         show : function(options)
5250         {
5251             
5252             // this causes nightmares if you show one dialog after another
5253             // especially on callbacks..
5254              
5255             if(this.isVisible()){
5256                 
5257                 this.hide();
5258                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5260                 Roo.log("New Dialog Message:" +  options.msg )
5261                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5263                 
5264             }
5265             var d = this.getDialog();
5266             opt = options;
5267             d.setTitle(opt.title || "&#160;");
5268             d.closeEl.setDisplayed(opt.closable !== false);
5269             activeTextEl = textboxEl;
5270             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5271             if(opt.prompt){
5272                 if(opt.multiline){
5273                     textboxEl.hide();
5274                     textareaEl.show();
5275                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5276                         opt.multiline : this.defaultTextHeight);
5277                     activeTextEl = textareaEl;
5278                 }else{
5279                     textboxEl.show();
5280                     textareaEl.hide();
5281                 }
5282             }else{
5283                 textboxEl.hide();
5284                 textareaEl.hide();
5285             }
5286             progressEl.setDisplayed(opt.progress === true);
5287             if (opt.progress) {
5288                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5289             }
5290             this.updateProgress(0);
5291             activeTextEl.dom.value = opt.value || "";
5292             if(opt.prompt){
5293                 dlg.setDefaultButton(activeTextEl);
5294             }else{
5295                 var bs = opt.buttons;
5296                 var db = null;
5297                 if(bs && bs.ok){
5298                     db = buttons["ok"];
5299                 }else if(bs && bs.yes){
5300                     db = buttons["yes"];
5301                 }
5302                 dlg.setDefaultButton(db);
5303             }
5304             bwidth = updateButtons(opt.buttons);
5305             this.updateText(opt.msg);
5306             if(opt.cls){
5307                 d.el.addClass(opt.cls);
5308             }
5309             d.proxyDrag = opt.proxyDrag === true;
5310             d.modal = opt.modal !== false;
5311             d.mask = opt.modal !== false ? mask : false;
5312             if(!d.isVisible()){
5313                 // force it to the end of the z-index stack so it gets a cursor in FF
5314                 document.body.appendChild(dlg.el.dom);
5315                 d.animateTarget = null;
5316                 d.show(options.animEl);
5317             }
5318             return this;
5319         },
5320
5321         /**
5322          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5323          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324          * and closing the message box when the process is complete.
5325          * @param {String} title The title bar text
5326          * @param {String} msg The message box body text
5327          * @return {Roo.MessageBox} This message box
5328          */
5329         progress : function(title, msg){
5330             this.show({
5331                 title : title,
5332                 msg : msg,
5333                 buttons: false,
5334                 progress:true,
5335                 closable:false,
5336                 minWidth: this.minProgressWidth,
5337                 modal : true
5338             });
5339             return this;
5340         },
5341
5342         /**
5343          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344          * If a callback function is passed it will be called after the user clicks the button, and the
5345          * id of the button that was clicked will be passed as the only parameter to the callback
5346          * (could also be the top-right close button).
5347          * @param {String} title The title bar text
5348          * @param {String} msg The message box body text
5349          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350          * @param {Object} scope (optional) The scope of the callback function
5351          * @return {Roo.MessageBox} This message box
5352          */
5353         alert : function(title, msg, fn, scope)
5354         {
5355             this.show({
5356                 title : title,
5357                 msg : msg,
5358                 buttons: this.OK,
5359                 fn: fn,
5360                 closable : false,
5361                 scope : scope,
5362                 modal : true
5363             });
5364             return this;
5365         },
5366
5367         /**
5368          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5369          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370          * You are responsible for closing the message box when the process is complete.
5371          * @param {String} msg The message box body text
5372          * @param {String} title (optional) The title bar text
5373          * @return {Roo.MessageBox} This message box
5374          */
5375         wait : function(msg, title){
5376             this.show({
5377                 title : title,
5378                 msg : msg,
5379                 buttons: false,
5380                 closable:false,
5381                 progress:true,
5382                 modal:true,
5383                 width:300,
5384                 wait:true
5385             });
5386             waitTimer = Roo.TaskMgr.start({
5387                 run: function(i){
5388                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5389                 },
5390                 interval: 1000
5391             });
5392             return this;
5393         },
5394
5395         /**
5396          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399          * @param {String} title The title bar text
5400          * @param {String} msg The message box body text
5401          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402          * @param {Object} scope (optional) The scope of the callback function
5403          * @return {Roo.MessageBox} This message box
5404          */
5405         confirm : function(title, msg, fn, scope){
5406             this.show({
5407                 title : title,
5408                 msg : msg,
5409                 buttons: this.YESNO,
5410                 fn: fn,
5411                 scope : scope,
5412                 modal : true
5413             });
5414             return this;
5415         },
5416
5417         /**
5418          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5420          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421          * (could also be the top-right close button) and the text that was entered will be passed as the two
5422          * parameters to the callback.
5423          * @param {String} title The title bar text
5424          * @param {String} msg The message box body text
5425          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426          * @param {Object} scope (optional) The scope of the callback function
5427          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429          * @return {Roo.MessageBox} This message box
5430          */
5431         prompt : function(title, msg, fn, scope, multiline){
5432             this.show({
5433                 title : title,
5434                 msg : msg,
5435                 buttons: this.OKCANCEL,
5436                 fn: fn,
5437                 minWidth:250,
5438                 scope : scope,
5439                 prompt:true,
5440                 multiline: multiline,
5441                 modal : true
5442             });
5443             return this;
5444         },
5445
5446         /**
5447          * Button config that displays a single OK button
5448          * @type Object
5449          */
5450         OK : {ok:true},
5451         /**
5452          * Button config that displays Yes and No buttons
5453          * @type Object
5454          */
5455         YESNO : {yes:true, no:true},
5456         /**
5457          * Button config that displays OK and Cancel buttons
5458          * @type Object
5459          */
5460         OKCANCEL : {ok:true, cancel:true},
5461         /**
5462          * Button config that displays Yes, No and Cancel buttons
5463          * @type Object
5464          */
5465         YESNOCANCEL : {yes:true, no:true, cancel:true},
5466
5467         /**
5468          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5469          * @type Number
5470          */
5471         defaultTextHeight : 75,
5472         /**
5473          * The maximum width in pixels of the message box (defaults to 600)
5474          * @type Number
5475          */
5476         maxWidth : 600,
5477         /**
5478          * The minimum width in pixels of the message box (defaults to 100)
5479          * @type Number
5480          */
5481         minWidth : 100,
5482         /**
5483          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5484          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5485          * @type Number
5486          */
5487         minProgressWidth : 250,
5488         /**
5489          * An object containing the default button text strings that can be overriden for localized language support.
5490          * Supported properties are: ok, cancel, yes and no.
5491          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5492          * @type Object
5493          */
5494         buttonText : {
5495             ok : "OK",
5496             cancel : "Cancel",
5497             yes : "Yes",
5498             no : "No"
5499         }
5500     };
5501 }();
5502
5503 /**
5504  * Shorthand for {@link Roo.MessageBox}
5505  */
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5508 /*
5509  * - LGPL
5510  *
5511  * navbar
5512  * 
5513  */
5514
5515 /**
5516  * @class Roo.bootstrap.Navbar
5517  * @extends Roo.bootstrap.Component
5518  * Bootstrap Navbar class
5519
5520  * @constructor
5521  * Create a new Navbar
5522  * @param {Object} config The config object
5523  */
5524
5525
5526 Roo.bootstrap.Navbar = function(config){
5527     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5528     this.addEvents({
5529         // raw events
5530         /**
5531          * @event beforetoggle
5532          * Fire before toggle the menu
5533          * @param {Roo.EventObject} e
5534          */
5535         "beforetoggle" : true
5536     });
5537 };
5538
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5540     
5541     
5542    
5543     // private
5544     navItems : false,
5545     loadMask : false,
5546     
5547     
5548     getAutoCreate : function(){
5549         
5550         
5551         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5552         
5553     },
5554     
5555     initEvents :function ()
5556     {
5557         //Roo.log(this.el.select('.navbar-toggle',true));
5558         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5559         
5560         var mark = {
5561             tag: "div",
5562             cls:"x-dlg-mask"
5563         };
5564         
5565         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5566         
5567         var size = this.el.getSize();
5568         this.maskEl.setSize(size.width, size.height);
5569         this.maskEl.enableDisplayMode("block");
5570         this.maskEl.hide();
5571         
5572         if(this.loadMask){
5573             this.maskEl.show();
5574         }
5575     },
5576     
5577     
5578     getChildContainer : function()
5579     {
5580         if (this.el && this.el.select('.collapse').getCount()) {
5581             return this.el.select('.collapse',true).first();
5582         }
5583         
5584         return this.el;
5585     },
5586     
5587     mask : function()
5588     {
5589         this.maskEl.show();
5590     },
5591     
5592     unmask : function()
5593     {
5594         this.maskEl.hide();
5595     },
5596     onToggle : function()
5597     {
5598         
5599         if(this.fireEvent('beforetoggle', this) === false){
5600             return;
5601         }
5602         var ce = this.el.select('.navbar-collapse',true).first();
5603       
5604         if (!ce.hasClass('show')) {
5605            this.expand();
5606         } else {
5607             this.collapse();
5608         }
5609         
5610         
5611     
5612     },
5613     /**
5614      * Expand the navbar pulldown 
5615      */
5616     expand : function ()
5617     {
5618        
5619         var ce = this.el.select('.navbar-collapse',true).first();
5620         if (ce.hasClass('collapsing')) {
5621             return;
5622         }
5623         ce.dom.style.height = '';
5624                // show it...
5625         ce.addClass('in'); // old...
5626         ce.removeClass('collapse');
5627         ce.addClass('show');
5628         var h = ce.getHeight();
5629         Roo.log(h);
5630         ce.removeClass('show');
5631         // at this point we should be able to see it..
5632         ce.addClass('collapsing');
5633         
5634         ce.setHeight(0); // resize it ...
5635         ce.on('transitionend', function() {
5636             //Roo.log('done transition');
5637             ce.removeClass('collapsing');
5638             ce.addClass('show');
5639             ce.removeClass('collapse');
5640
5641             ce.dom.style.height = '';
5642         }, this, { single: true} );
5643         ce.setHeight(h);
5644         ce.dom.scrollTop = 0;
5645     },
5646     /**
5647      * Collapse the navbar pulldown 
5648      */
5649     collapse : function()
5650     {
5651          var ce = this.el.select('.navbar-collapse',true).first();
5652        
5653         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654             // it's collapsed or collapsing..
5655             return;
5656         }
5657         ce.removeClass('in'); // old...
5658         ce.setHeight(ce.getHeight());
5659         ce.removeClass('show');
5660         ce.addClass('collapsing');
5661         
5662         ce.on('transitionend', function() {
5663             ce.dom.style.height = '';
5664             ce.removeClass('collapsing');
5665             ce.addClass('collapse');
5666         }, this, { single: true} );
5667         ce.setHeight(0);
5668     }
5669     
5670     
5671     
5672 });
5673
5674
5675
5676  
5677
5678  /*
5679  * - LGPL
5680  *
5681  * navbar
5682  * 
5683  */
5684
5685 /**
5686  * @class Roo.bootstrap.NavSimplebar
5687  * @extends Roo.bootstrap.Navbar
5688  * Bootstrap Sidebar class
5689  *
5690  * @cfg {Boolean} inverse is inverted color
5691  * 
5692  * @cfg {String} type (nav | pills | tabs)
5693  * @cfg {Boolean} arrangement stacked | justified
5694  * @cfg {String} align (left | right) alignment
5695  * 
5696  * @cfg {Boolean} main (true|false) main nav bar? default false
5697  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5698  * 
5699  * @cfg {String} tag (header|footer|nav|div) default is nav 
5700
5701  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5702  * 
5703  * 
5704  * @constructor
5705  * Create a new Sidebar
5706  * @param {Object} config The config object
5707  */
5708
5709
5710 Roo.bootstrap.NavSimplebar = function(config){
5711     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5712 };
5713
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5715     
5716     inverse: false,
5717     
5718     type: false,
5719     arrangement: '',
5720     align : false,
5721     
5722     weight : 'light',
5723     
5724     main : false,
5725     
5726     
5727     tag : false,
5728     
5729     
5730     getAutoCreate : function(){
5731         
5732         
5733         var cfg = {
5734             tag : this.tag || 'div',
5735             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5736         };
5737         if (['light','white'].indexOf(this.weight) > -1) {
5738             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5739         }
5740         cfg.cls += ' bg-' + this.weight;
5741         
5742         if (this.inverse) {
5743             cfg.cls += ' navbar-inverse';
5744             
5745         }
5746         
5747         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5748         
5749         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5750             return cfg;
5751         }
5752         
5753         
5754     
5755         
5756         cfg.cn = [
5757             {
5758                 cls: 'nav nav-' + this.xtype,
5759                 tag : 'ul'
5760             }
5761         ];
5762         
5763          
5764         this.type = this.type || 'nav';
5765         if (['tabs','pills'].indexOf(this.type) != -1) {
5766             cfg.cn[0].cls += ' nav-' + this.type
5767         
5768         
5769         } else {
5770             if (this.type!=='nav') {
5771                 Roo.log('nav type must be nav/tabs/pills')
5772             }
5773             cfg.cn[0].cls += ' navbar-nav'
5774         }
5775         
5776         
5777         
5778         
5779         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780             cfg.cn[0].cls += ' nav-' + this.arrangement;
5781         }
5782         
5783         
5784         if (this.align === 'right') {
5785             cfg.cn[0].cls += ' navbar-right';
5786         }
5787         
5788         
5789         
5790         
5791         return cfg;
5792     
5793         
5794     }
5795     
5796     
5797     
5798 });
5799
5800
5801
5802  
5803
5804  
5805        /*
5806  * - LGPL
5807  *
5808  * navbar
5809  * navbar-fixed-top
5810  * navbar-expand-md  fixed-top 
5811  */
5812
5813 /**
5814  * @class Roo.bootstrap.NavHeaderbar
5815  * @extends Roo.bootstrap.NavSimplebar
5816  * Bootstrap Sidebar class
5817  *
5818  * @cfg {String} brand what is brand
5819  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820  * @cfg {String} brand_href href of the brand
5821  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5822  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5825  * 
5826  * @constructor
5827  * Create a new Sidebar
5828  * @param {Object} config The config object
5829  */
5830
5831
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5834       
5835 };
5836
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5838     
5839     position: '',
5840     brand: '',
5841     brand_href: false,
5842     srButton : true,
5843     autohide : false,
5844     desktopCenter : false,
5845    
5846     
5847     getAutoCreate : function(){
5848         
5849         var   cfg = {
5850             tag: this.nav || 'nav',
5851             cls: 'navbar navbar-expand-md',
5852             role: 'navigation',
5853             cn: []
5854         };
5855         
5856         var cn = cfg.cn;
5857         if (this.desktopCenter) {
5858             cn.push({cls : 'container', cn : []});
5859             cn = cn[0].cn;
5860         }
5861         
5862         if(this.srButton){
5863             var btn = {
5864                 tag: 'button',
5865                 type: 'button',
5866                 cls: 'navbar-toggle navbar-toggler',
5867                 'data-toggle': 'collapse',
5868                 cn: [
5869                     {
5870                         tag: 'span',
5871                         cls: 'sr-only',
5872                         html: 'Toggle navigation'
5873                     },
5874                     {
5875                         tag: 'span',
5876                         cls: 'icon-bar navbar-toggler-icon'
5877                     },
5878                     {
5879                         tag: 'span',
5880                         cls: 'icon-bar'
5881                     },
5882                     {
5883                         tag: 'span',
5884                         cls: 'icon-bar'
5885                     }
5886                 ]
5887             };
5888             
5889             cn.push( Roo.bootstrap.version == 4 ? btn : {
5890                 tag: 'div',
5891                 cls: 'navbar-header',
5892                 cn: [
5893                     btn
5894                 ]
5895             });
5896         }
5897         
5898         cn.push({
5899             tag: 'div',
5900             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5901             cn : []
5902         });
5903         
5904         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5905         
5906         if (['light','white'].indexOf(this.weight) > -1) {
5907             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5908         }
5909         cfg.cls += ' bg-' + this.weight;
5910         
5911         
5912         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5914             
5915             // tag can override this..
5916             
5917             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5918         }
5919         
5920         if (this.brand !== '') {
5921             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5923                 tag: 'a',
5924                 href: this.brand_href ? this.brand_href : '#',
5925                 cls: 'navbar-brand',
5926                 cn: [
5927                 this.brand
5928                 ]
5929             });
5930         }
5931         
5932         if(this.main){
5933             cfg.cls += ' main-nav';
5934         }
5935         
5936         
5937         return cfg;
5938
5939         
5940     },
5941     getHeaderChildContainer : function()
5942     {
5943         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944             return this.el.select('.navbar-header',true).first();
5945         }
5946         
5947         return this.getChildContainer();
5948     },
5949     
5950     getChildContainer : function()
5951     {
5952          
5953         return this.el.select('.roo-navbar-collapse',true).first();
5954          
5955         
5956     },
5957     
5958     initEvents : function()
5959     {
5960         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5961         
5962         if (this.autohide) {
5963             
5964             var prevScroll = 0;
5965             var ft = this.el;
5966             
5967             Roo.get(document).on('scroll',function(e) {
5968                 var ns = Roo.get(document).getScroll().top;
5969                 var os = prevScroll;
5970                 prevScroll = ns;
5971                 
5972                 if(ns > os){
5973                     ft.removeClass('slideDown');
5974                     ft.addClass('slideUp');
5975                     return;
5976                 }
5977                 ft.removeClass('slideUp');
5978                 ft.addClass('slideDown');
5979                  
5980               
5981           },this);
5982         }
5983     }    
5984     
5985 });
5986
5987
5988
5989  
5990
5991  /*
5992  * - LGPL
5993  *
5994  * navbar
5995  * 
5996  */
5997
5998 /**
5999  * @class Roo.bootstrap.NavSidebar
6000  * @extends Roo.bootstrap.Navbar
6001  * Bootstrap Sidebar class
6002  * 
6003  * @constructor
6004  * Create a new Sidebar
6005  * @param {Object} config The config object
6006  */
6007
6008
6009 Roo.bootstrap.NavSidebar = function(config){
6010     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6011 };
6012
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6014     
6015     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6016     
6017     getAutoCreate : function(){
6018         
6019         
6020         return  {
6021             tag: 'div',
6022             cls: 'sidebar sidebar-nav'
6023         };
6024     
6025         
6026     }
6027     
6028     
6029     
6030 });
6031
6032
6033
6034  
6035
6036  /*
6037  * - LGPL
6038  *
6039  * nav group
6040  * 
6041  */
6042
6043 /**
6044  * @class Roo.bootstrap.NavGroup
6045  * @extends Roo.bootstrap.Component
6046  * Bootstrap NavGroup class
6047  * @cfg {String} align (left|right)
6048  * @cfg {Boolean} inverse
6049  * @cfg {String} type (nav|pills|tab) default nav
6050  * @cfg {String} navId - reference Id for navbar.
6051  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6052  * 
6053  * @constructor
6054  * Create a new nav group
6055  * @param {Object} config The config object
6056  */
6057
6058 Roo.bootstrap.NavGroup = function(config){
6059     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6060     this.navItems = [];
6061    
6062     Roo.bootstrap.NavGroup.register(this);
6063      this.addEvents({
6064         /**
6065              * @event changed
6066              * Fires when the active item changes
6067              * @param {Roo.bootstrap.NavGroup} this
6068              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6070          */
6071         'changed': true
6072      });
6073     
6074 };
6075
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6077     
6078     align: '',
6079     inverse: false,
6080     form: false,
6081     type: 'nav',
6082     navId : '',
6083     // private
6084     pilltype : true,
6085     
6086     navItems : false, 
6087     
6088     getAutoCreate : function()
6089     {
6090         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6091         
6092         cfg = {
6093             tag : 'ul',
6094             cls: 'nav' 
6095         };
6096         if (Roo.bootstrap.version == 4) {
6097             if (['tabs','pills'].indexOf(this.type) != -1) {
6098                 cfg.cls += ' nav-' + this.type; 
6099             } else {
6100                 // trying to remove so header bar can right align top?
6101                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102                     // do not use on header bar... 
6103                     cfg.cls += ' navbar-nav';
6104                 }
6105             }
6106             
6107         } else {
6108             if (['tabs','pills'].indexOf(this.type) != -1) {
6109                 cfg.cls += ' nav-' + this.type
6110             } else {
6111                 if (this.type !== 'nav') {
6112                     Roo.log('nav type must be nav/tabs/pills')
6113                 }
6114                 cfg.cls += ' navbar-nav'
6115             }
6116         }
6117         
6118         if (this.parent() && this.parent().sidebar) {
6119             cfg = {
6120                 tag: 'ul',
6121                 cls: 'dashboard-menu sidebar-menu'
6122             };
6123             
6124             return cfg;
6125         }
6126         
6127         if (this.form === true) {
6128             cfg = {
6129                 tag: 'form',
6130                 cls: 'navbar-form form-inline'
6131             };
6132             //nav navbar-right ml-md-auto
6133             if (this.align === 'right') {
6134                 cfg.cls += ' navbar-right ml-md-auto';
6135             } else {
6136                 cfg.cls += ' navbar-left';
6137             }
6138         }
6139         
6140         if (this.align === 'right') {
6141             cfg.cls += ' navbar-right ml-md-auto';
6142         } else {
6143             cfg.cls += ' mr-auto';
6144         }
6145         
6146         if (this.inverse) {
6147             cfg.cls += ' navbar-inverse';
6148             
6149         }
6150         
6151         
6152         return cfg;
6153     },
6154     /**
6155     * sets the active Navigation item
6156     * @param {Roo.bootstrap.NavItem} the new current navitem
6157     */
6158     setActiveItem : function(item)
6159     {
6160         var prev = false;
6161         Roo.each(this.navItems, function(v){
6162             if (v == item) {
6163                 return ;
6164             }
6165             if (v.isActive()) {
6166                 v.setActive(false, true);
6167                 prev = v;
6168                 
6169             }
6170             
6171         });
6172
6173         item.setActive(true, true);
6174         this.fireEvent('changed', this, item, prev);
6175         
6176         
6177     },
6178     /**
6179     * gets the active Navigation item
6180     * @return {Roo.bootstrap.NavItem} the current navitem
6181     */
6182     getActive : function()
6183     {
6184         
6185         var prev = false;
6186         Roo.each(this.navItems, function(v){
6187             
6188             if (v.isActive()) {
6189                 prev = v;
6190                 
6191             }
6192             
6193         });
6194         return prev;
6195     },
6196     
6197     indexOfNav : function()
6198     {
6199         
6200         var prev = false;
6201         Roo.each(this.navItems, function(v,i){
6202             
6203             if (v.isActive()) {
6204                 prev = i;
6205                 
6206             }
6207             
6208         });
6209         return prev;
6210     },
6211     /**
6212     * adds a Navigation item
6213     * @param {Roo.bootstrap.NavItem} the navitem to add
6214     */
6215     addItem : function(cfg)
6216     {
6217         if (this.form && Roo.bootstrap.version == 4) {
6218             cfg.tag = 'div';
6219         }
6220         var cn = new Roo.bootstrap.NavItem(cfg);
6221         this.register(cn);
6222         cn.parentId = this.id;
6223         cn.onRender(this.el, null);
6224         return cn;
6225     },
6226     /**
6227     * register a Navigation item
6228     * @param {Roo.bootstrap.NavItem} the navitem to add
6229     */
6230     register : function(item)
6231     {
6232         this.navItems.push( item);
6233         item.navId = this.navId;
6234     
6235     },
6236     
6237     /**
6238     * clear all the Navigation item
6239     */
6240    
6241     clearAll : function()
6242     {
6243         this.navItems = [];
6244         this.el.dom.innerHTML = '';
6245     },
6246     
6247     getNavItem: function(tabId)
6248     {
6249         var ret = false;
6250         Roo.each(this.navItems, function(e) {
6251             if (e.tabId == tabId) {
6252                ret =  e;
6253                return false;
6254             }
6255             return true;
6256             
6257         });
6258         return ret;
6259     },
6260     
6261     setActiveNext : function()
6262     {
6263         var i = this.indexOfNav(this.getActive());
6264         if (i > this.navItems.length) {
6265             return;
6266         }
6267         this.setActiveItem(this.navItems[i+1]);
6268     },
6269     setActivePrev : function()
6270     {
6271         var i = this.indexOfNav(this.getActive());
6272         if (i  < 1) {
6273             return;
6274         }
6275         this.setActiveItem(this.navItems[i-1]);
6276     },
6277     clearWasActive : function(except) {
6278         Roo.each(this.navItems, function(e) {
6279             if (e.tabId != except.tabId && e.was_active) {
6280                e.was_active = false;
6281                return false;
6282             }
6283             return true;
6284             
6285         });
6286     },
6287     getWasActive : function ()
6288     {
6289         var r = false;
6290         Roo.each(this.navItems, function(e) {
6291             if (e.was_active) {
6292                r = e;
6293                return false;
6294             }
6295             return true;
6296             
6297         });
6298         return r;
6299     }
6300     
6301     
6302 });
6303
6304  
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6306     
6307     groups: {},
6308      /**
6309     * register a Navigation Group
6310     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6311     */
6312     register : function(navgrp)
6313     {
6314         this.groups[navgrp.navId] = navgrp;
6315         
6316     },
6317     /**
6318     * fetch a Navigation Group based on the navigation ID
6319     * @param {string} the navgroup to add
6320     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6321     */
6322     get: function(navId) {
6323         if (typeof(this.groups[navId]) == 'undefined') {
6324             return false;
6325             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6326         }
6327         return this.groups[navId] ;
6328     }
6329     
6330     
6331     
6332 });
6333
6334  /*
6335  * - LGPL
6336  *
6337  * row
6338  * 
6339  */
6340
6341 /**
6342  * @class Roo.bootstrap.NavItem
6343  * @extends Roo.bootstrap.Component
6344  * Bootstrap Navbar.NavItem class
6345  * @cfg {String} href  link to
6346  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347  * @cfg {Boolean} button_outline show and outlined button
6348  * @cfg {String} html content of button
6349  * @cfg {String} badge text inside badge
6350  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351  * @cfg {String} glyphicon DEPRICATED - use fa
6352  * @cfg {String} icon DEPRICATED - use fa
6353  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354  * @cfg {Boolean} active Is item active
6355  * @cfg {Boolean} disabled Is item disabled
6356  * @cfg {String} linkcls  Link Class
6357  * @cfg {Boolean} preventDefault (true | false) default false
6358  * @cfg {String} tabId the tab that this item activates.
6359  * @cfg {String} tagtype (a|span) render as a href or span?
6360  * @cfg {Boolean} animateRef (true|false) link to element default false  
6361   
6362  * @constructor
6363  * Create a new Navbar Item
6364  * @param {Object} config The config object
6365  */
6366 Roo.bootstrap.NavItem = function(config){
6367     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6368     this.addEvents({
6369         // raw events
6370         /**
6371          * @event click
6372          * The raw click event for the entire grid.
6373          * @param {Roo.EventObject} e
6374          */
6375         "click" : true,
6376          /**
6377             * @event changed
6378             * Fires when the active item active state changes
6379             * @param {Roo.bootstrap.NavItem} this
6380             * @param {boolean} state the new state
6381              
6382          */
6383         'changed': true,
6384         /**
6385             * @event scrollto
6386             * Fires when scroll to element
6387             * @param {Roo.bootstrap.NavItem} this
6388             * @param {Object} options
6389             * @param {Roo.EventObject} e
6390              
6391          */
6392         'scrollto': true
6393     });
6394    
6395 };
6396
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6398     
6399     href: false,
6400     html: '',
6401     badge: '',
6402     icon: false,
6403     fa : false,
6404     glyphicon: false,
6405     active: false,
6406     preventDefault : false,
6407     tabId : false,
6408     tagtype : 'a',
6409     tag: 'li',
6410     disabled : false,
6411     animateRef : false,
6412     was_active : false,
6413     button_weight : '',
6414     button_outline : false,
6415     linkcls : '',
6416     navLink: false,
6417     
6418     getAutoCreate : function(){
6419          
6420         var cfg = {
6421             tag: this.tag,
6422             cls: 'nav-item'
6423         };
6424         
6425         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6426         
6427         if (this.active) {
6428             cfg.cls +=  ' active' ;
6429         }
6430         if (this.disabled) {
6431             cfg.cls += ' disabled';
6432         }
6433         
6434         // BS4 only?
6435         if (this.button_weight.length) {
6436             cfg.tag = this.href ? 'a' : 'button';
6437             cfg.html = this.html || '';
6438             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6439             if (this.href) {
6440                 cfg.href = this.href;
6441             }
6442             if (this.fa) {
6443                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6444             } else {
6445                 cfg.cls += " nav-html";
6446             }
6447             
6448             // menu .. should add dropdown-menu class - so no need for carat..
6449             
6450             if (this.badge !== '') {
6451                  
6452                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6453             }
6454             return cfg;
6455         }
6456         
6457         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6458             cfg.cn = [
6459                 {
6460                     tag: this.tagtype,
6461                     href : this.href || "#",
6462                     html: this.html || '',
6463                     cls : ''
6464                 }
6465             ];
6466             if (this.tagtype == 'a') {
6467                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6468         
6469             }
6470             if (this.icon) {
6471                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472             } else  if (this.fa) {
6473                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474             } else if(this.glyphicon) {
6475                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6476             } else {
6477                 cfg.cn[0].cls += " nav-html";
6478             }
6479             
6480             if (this.menu) {
6481                 cfg.cn[0].html += " <span class='caret'></span>";
6482              
6483             }
6484             
6485             if (this.badge !== '') {
6486                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6487             }
6488         }
6489         
6490         
6491         
6492         return cfg;
6493     },
6494     onRender : function(ct, position)
6495     {
6496        // Roo.log("Call onRender: " + this.xtype);
6497         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6498             this.tag = 'div';
6499         }
6500         
6501         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502         this.navLink = this.el.select('.nav-link',true).first();
6503         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6504         return ret;
6505     },
6506       
6507     
6508     initEvents: function() 
6509     {
6510         if (typeof (this.menu) != 'undefined') {
6511             this.menu.parentType = this.xtype;
6512             this.menu.triggerEl = this.el;
6513             this.menu = this.addxtype(Roo.apply({}, this.menu));
6514         }
6515         
6516         this.el.on('click', this.onClick, this);
6517         
6518         //if(this.tagtype == 'span'){
6519         //    this.el.select('span',true).on('click', this.onClick, this);
6520         //}
6521        
6522         // at this point parent should be available..
6523         this.parent().register(this);
6524     },
6525     
6526     onClick : function(e)
6527     {
6528         if (e.getTarget('.dropdown-menu-item')) {
6529             // did you click on a menu itemm.... - then don't trigger onclick..
6530             return;
6531         }
6532         
6533         if(
6534                 this.preventDefault || 
6535                 this.href == '#' 
6536         ){
6537             Roo.log("NavItem - prevent Default?");
6538             e.preventDefault();
6539         }
6540         
6541         if (this.disabled) {
6542             return;
6543         }
6544         
6545         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546         if (tg && tg.transition) {
6547             Roo.log("waiting for the transitionend");
6548             return;
6549         }
6550         
6551         
6552         
6553         //Roo.log("fire event clicked");
6554         if(this.fireEvent('click', this, e) === false){
6555             return;
6556         };
6557         
6558         if(this.tagtype == 'span'){
6559             return;
6560         }
6561         
6562         //Roo.log(this.href);
6563         var ael = this.el.select('a',true).first();
6564         //Roo.log(ael);
6565         
6566         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569                 return; // ignore... - it's a 'hash' to another page.
6570             }
6571             Roo.log("NavItem - prevent Default?");
6572             e.preventDefault();
6573             this.scrollToElement(e);
6574         }
6575         
6576         
6577         var p =  this.parent();
6578    
6579         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580             if (typeof(p.setActiveItem) !== 'undefined') {
6581                 p.setActiveItem(this);
6582             }
6583         }
6584         
6585         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587             // remove the collapsed menu expand...
6588             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6589         }
6590     },
6591     
6592     isActive: function () {
6593         return this.active
6594     },
6595     setActive : function(state, fire, is_was_active)
6596     {
6597         if (this.active && !state && this.navId) {
6598             this.was_active = true;
6599             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6600             if (nv) {
6601                 nv.clearWasActive(this);
6602             }
6603             
6604         }
6605         this.active = state;
6606         
6607         if (!state ) {
6608             this.el.removeClass('active');
6609             this.navLink ? this.navLink.removeClass('active') : false;
6610         } else if (!this.el.hasClass('active')) {
6611             
6612             this.el.addClass('active');
6613             if (Roo.bootstrap.version == 4 && this.navLink ) {
6614                 this.navLink.addClass('active');
6615             }
6616             
6617         }
6618         if (fire) {
6619             this.fireEvent('changed', this, state);
6620         }
6621         
6622         // show a panel if it's registered and related..
6623         
6624         if (!this.navId || !this.tabId || !state || is_was_active) {
6625             return;
6626         }
6627         
6628         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6629         if (!tg) {
6630             return;
6631         }
6632         var pan = tg.getPanelByName(this.tabId);
6633         if (!pan) {
6634             return;
6635         }
6636         // if we can not flip to new panel - go back to old nav highlight..
6637         if (false == tg.showPanel(pan)) {
6638             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6639             if (nv) {
6640                 var onav = nv.getWasActive();
6641                 if (onav) {
6642                     onav.setActive(true, false, true);
6643                 }
6644             }
6645             
6646         }
6647         
6648         
6649         
6650     },
6651      // this should not be here...
6652     setDisabled : function(state)
6653     {
6654         this.disabled = state;
6655         if (!state ) {
6656             this.el.removeClass('disabled');
6657         } else if (!this.el.hasClass('disabled')) {
6658             this.el.addClass('disabled');
6659         }
6660         
6661     },
6662     
6663     /**
6664      * Fetch the element to display the tooltip on.
6665      * @return {Roo.Element} defaults to this.el
6666      */
6667     tooltipEl : function()
6668     {
6669         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6670     },
6671     
6672     scrollToElement : function(e)
6673     {
6674         var c = document.body;
6675         
6676         /*
6677          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6678          */
6679         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680             c = document.documentElement;
6681         }
6682         
6683         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6684         
6685         if(!target){
6686             return;
6687         }
6688
6689         var o = target.calcOffsetsTo(c);
6690         
6691         var options = {
6692             target : target,
6693             value : o[1]
6694         };
6695         
6696         this.fireEvent('scrollto', this, options, e);
6697         
6698         Roo.get(c).scrollTo('top', options.value, true);
6699         
6700         return;
6701     },
6702     /**
6703      * Set the HTML (text content) of the item
6704      * @param {string} html  content for the nav item
6705      */
6706     setHtml : function(html)
6707     {
6708         this.html = html;
6709         this.htmlEl.dom.innerHTML = html;
6710         
6711     } 
6712 });
6713  
6714
6715  /*
6716  * - LGPL
6717  *
6718  * sidebar item
6719  *
6720  *  li
6721  *    <span> icon </span>
6722  *    <span> text </span>
6723  *    <span>badge </span>
6724  */
6725
6726 /**
6727  * @class Roo.bootstrap.NavSidebarItem
6728  * @extends Roo.bootstrap.NavItem
6729  * Bootstrap Navbar.NavSidebarItem class
6730  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731  * {Boolean} open is the menu open
6732  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734  * {String} buttonSize (sm|md|lg)the extra classes for the button
6735  * {Boolean} showArrow show arrow next to the text (default true)
6736  * @constructor
6737  * Create a new Navbar Button
6738  * @param {Object} config The config object
6739  */
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6742     this.addEvents({
6743         // raw events
6744         /**
6745          * @event click
6746          * The raw click event for the entire grid.
6747          * @param {Roo.EventObject} e
6748          */
6749         "click" : true,
6750          /**
6751             * @event changed
6752             * Fires when the active item active state changes
6753             * @param {Roo.bootstrap.NavSidebarItem} this
6754             * @param {boolean} state the new state
6755              
6756          */
6757         'changed': true
6758     });
6759    
6760 };
6761
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6763     
6764     badgeWeight : 'default',
6765     
6766     open: false,
6767     
6768     buttonView : false,
6769     
6770     buttonWeight : 'default',
6771     
6772     buttonSize : 'md',
6773     
6774     showArrow : true,
6775     
6776     getAutoCreate : function(){
6777         
6778         
6779         var a = {
6780                 tag: 'a',
6781                 href : this.href || '#',
6782                 cls: '',
6783                 html : '',
6784                 cn : []
6785         };
6786         
6787         if(this.buttonView){
6788             a = {
6789                 tag: 'button',
6790                 href : this.href || '#',
6791                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6792                 html : this.html,
6793                 cn : []
6794             };
6795         }
6796         
6797         var cfg = {
6798             tag: 'li',
6799             cls: '',
6800             cn: [ a ]
6801         };
6802         
6803         if (this.active) {
6804             cfg.cls += ' active';
6805         }
6806         
6807         if (this.disabled) {
6808             cfg.cls += ' disabled';
6809         }
6810         if (this.open) {
6811             cfg.cls += ' open x-open';
6812         }
6813         // left icon..
6814         if (this.glyphicon || this.icon) {
6815             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6816             a.cn.push({ tag : 'i', cls : c }) ;
6817         }
6818         
6819         if(!this.buttonView){
6820             var span = {
6821                 tag: 'span',
6822                 html : this.html || ''
6823             };
6824
6825             a.cn.push(span);
6826             
6827         }
6828         
6829         if (this.badge !== '') {
6830             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6831         }
6832         
6833         if (this.menu) {
6834             
6835             if(this.showArrow){
6836                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6837             }
6838             
6839             a.cls += ' dropdown-toggle treeview' ;
6840         }
6841         
6842         return cfg;
6843     },
6844     
6845     initEvents : function()
6846     { 
6847         if (typeof (this.menu) != 'undefined') {
6848             this.menu.parentType = this.xtype;
6849             this.menu.triggerEl = this.el;
6850             this.menu = this.addxtype(Roo.apply({}, this.menu));
6851         }
6852         
6853         this.el.on('click', this.onClick, this);
6854         
6855         if(this.badge !== ''){
6856             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6857         }
6858         
6859     },
6860     
6861     onClick : function(e)
6862     {
6863         if(this.disabled){
6864             e.preventDefault();
6865             return;
6866         }
6867         
6868         if(this.preventDefault){
6869             e.preventDefault();
6870         }
6871         
6872         this.fireEvent('click', this, e);
6873     },
6874     
6875     disable : function()
6876     {
6877         this.setDisabled(true);
6878     },
6879     
6880     enable : function()
6881     {
6882         this.setDisabled(false);
6883     },
6884     
6885     setDisabled : function(state)
6886     {
6887         if(this.disabled == state){
6888             return;
6889         }
6890         
6891         this.disabled = state;
6892         
6893         if (state) {
6894             this.el.addClass('disabled');
6895             return;
6896         }
6897         
6898         this.el.removeClass('disabled');
6899         
6900         return;
6901     },
6902     
6903     setActive : function(state)
6904     {
6905         if(this.active == state){
6906             return;
6907         }
6908         
6909         this.active = state;
6910         
6911         if (state) {
6912             this.el.addClass('active');
6913             return;
6914         }
6915         
6916         this.el.removeClass('active');
6917         
6918         return;
6919     },
6920     
6921     isActive: function () 
6922     {
6923         return this.active;
6924     },
6925     
6926     setBadge : function(str)
6927     {
6928         if(!this.badgeEl){
6929             return;
6930         }
6931         
6932         this.badgeEl.dom.innerHTML = str;
6933     }
6934     
6935    
6936      
6937  
6938 });
6939  
6940
6941  /*
6942  * - LGPL
6943  *
6944  *  Breadcrumb Nav
6945  * 
6946  */
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6948
6949
6950 /**
6951  * @class Roo.bootstrap.breadcrumb.Nav
6952  * @extends Roo.bootstrap.Component
6953  * Bootstrap Breadcrumb Nav Class
6954  *  
6955  * @children Roo.bootstrap.breadcrumb.Item
6956  * 
6957  * @constructor
6958  * Create a new breadcrumb.Nav
6959  * @param {Object} config The config object
6960  */
6961
6962
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6965     
6966     
6967 };
6968
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6970     
6971     getAutoCreate : function()
6972     {
6973
6974         var cfg = {
6975             tag: 'nav',
6976             cn : [
6977                 {
6978                     tag : 'ol',
6979                     cls : 'breadcrumb'
6980                 }
6981             ]
6982             
6983         };
6984           
6985         return cfg;
6986     },
6987     
6988     initEvents: function()
6989     {
6990         this.olEl = this.el.select('ol',true).first();    
6991     },
6992     getChildContainer : function()
6993     {
6994         return this.olEl;  
6995     }
6996     
6997 });
6998
6999  /*
7000  * - LGPL
7001  *
7002  *  Breadcrumb Item
7003  * 
7004  */
7005
7006
7007 /**
7008  * @class Roo.bootstrap.breadcrumb.Nav
7009  * @extends Roo.bootstrap.Component
7010  * Bootstrap Breadcrumb Nav Class
7011  *  
7012  * @children Roo.bootstrap.breadcrumb.Component
7013  * @cfg {String} html the content of the link.
7014  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015  * @cfg {Boolean} active is it active
7016
7017  * 
7018  * @constructor
7019  * Create a new breadcrumb.Nav
7020  * @param {Object} config The config object
7021  */
7022
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7025     this.addEvents({
7026         // img events
7027         /**
7028          * @event click
7029          * The img click event for the img.
7030          * @param {Roo.EventObject} e
7031          */
7032         "click" : true
7033     });
7034     
7035 };
7036
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7038     
7039     href: false,
7040     html : '',
7041     
7042     getAutoCreate : function()
7043     {
7044
7045         var cfg = {
7046             tag: 'li',
7047             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7048         };
7049         if (this.href !== false) {
7050             cfg.cn = [{
7051                 tag : 'a',
7052                 href : this.href,
7053                 html : this.html
7054             }];
7055         } else {
7056             cfg.html = this.html;
7057         }
7058         
7059         return cfg;
7060     },
7061     
7062     initEvents: function()
7063     {
7064         if (this.href) {
7065             this.el.select('a', true).first().on('click',this.onClick, this)
7066         }
7067         
7068     },
7069     onClick : function(e)
7070     {
7071         e.preventDefault();
7072         this.fireEvent('click',this,  e);
7073     }
7074     
7075 });
7076
7077  /*
7078  * - LGPL
7079  *
7080  * row
7081  * 
7082  */
7083
7084 /**
7085  * @class Roo.bootstrap.Row
7086  * @extends Roo.bootstrap.Component
7087  * Bootstrap Row class (contains columns...)
7088  * 
7089  * @constructor
7090  * Create a new Row
7091  * @param {Object} config The config object
7092  */
7093
7094 Roo.bootstrap.Row = function(config){
7095     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7096 };
7097
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7099     
7100     getAutoCreate : function(){
7101        return {
7102             cls: 'row clearfix'
7103        };
7104     }
7105     
7106     
7107 });
7108
7109  
7110
7111  /*
7112  * - LGPL
7113  *
7114  * pagination
7115  * 
7116  */
7117
7118 /**
7119  * @class Roo.bootstrap.Pagination
7120  * @extends Roo.bootstrap.Component
7121  * Bootstrap Pagination class
7122  * @cfg {String} size xs | sm | md | lg
7123  * @cfg {Boolean} inverse false | true
7124  * 
7125  * @constructor
7126  * Create a new Pagination
7127  * @param {Object} config The config object
7128  */
7129
7130 Roo.bootstrap.Pagination = function(config){
7131     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7132 };
7133
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7135     
7136     cls: false,
7137     size: false,
7138     inverse: false,
7139     
7140     getAutoCreate : function(){
7141         var cfg = {
7142             tag: 'ul',
7143                 cls: 'pagination'
7144         };
7145         if (this.inverse) {
7146             cfg.cls += ' inverse';
7147         }
7148         if (this.html) {
7149             cfg.html=this.html;
7150         }
7151         if (this.cls) {
7152             cfg.cls += " " + this.cls;
7153         }
7154         return cfg;
7155     }
7156    
7157 });
7158
7159  
7160
7161  /*
7162  * - LGPL
7163  *
7164  * Pagination item
7165  * 
7166  */
7167
7168
7169 /**
7170  * @class Roo.bootstrap.PaginationItem
7171  * @extends Roo.bootstrap.Component
7172  * Bootstrap PaginationItem class
7173  * @cfg {String} html text
7174  * @cfg {String} href the link
7175  * @cfg {Boolean} preventDefault (true | false) default true
7176  * @cfg {Boolean} active (true | false) default false
7177  * @cfg {Boolean} disabled default false
7178  * 
7179  * 
7180  * @constructor
7181  * Create a new PaginationItem
7182  * @param {Object} config The config object
7183  */
7184
7185
7186 Roo.bootstrap.PaginationItem = function(config){
7187     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7188     this.addEvents({
7189         // raw events
7190         /**
7191          * @event click
7192          * The raw click event for the entire grid.
7193          * @param {Roo.EventObject} e
7194          */
7195         "click" : true
7196     });
7197 };
7198
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7200     
7201     href : false,
7202     html : false,
7203     preventDefault: true,
7204     active : false,
7205     cls : false,
7206     disabled: false,
7207     
7208     getAutoCreate : function(){
7209         var cfg= {
7210             tag: 'li',
7211             cn: [
7212                 {
7213                     tag : 'a',
7214                     href : this.href ? this.href : '#',
7215                     html : this.html ? this.html : ''
7216                 }
7217             ]
7218         };
7219         
7220         if(this.cls){
7221             cfg.cls = this.cls;
7222         }
7223         
7224         if(this.disabled){
7225             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7226         }
7227         
7228         if(this.active){
7229             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7230         }
7231         
7232         return cfg;
7233     },
7234     
7235     initEvents: function() {
7236         
7237         this.el.on('click', this.onClick, this);
7238         
7239     },
7240     onClick : function(e)
7241     {
7242         Roo.log('PaginationItem on click ');
7243         if(this.preventDefault){
7244             e.preventDefault();
7245         }
7246         
7247         if(this.disabled){
7248             return;
7249         }
7250         
7251         this.fireEvent('click', this, e);
7252     }
7253    
7254 });
7255
7256  
7257
7258  /*
7259  * - LGPL
7260  *
7261  * slider
7262  * 
7263  */
7264
7265
7266 /**
7267  * @class Roo.bootstrap.Slider
7268  * @extends Roo.bootstrap.Component
7269  * Bootstrap Slider class
7270  *    
7271  * @constructor
7272  * Create a new Slider
7273  * @param {Object} config The config object
7274  */
7275
7276 Roo.bootstrap.Slider = function(config){
7277     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7278 };
7279
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7281     
7282     getAutoCreate : function(){
7283         
7284         var cfg = {
7285             tag: 'div',
7286             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7287             cn: [
7288                 {
7289                     tag: 'a',
7290                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7291                 }
7292             ]
7293         };
7294         
7295         return cfg;
7296     }
7297    
7298 });
7299
7300  /*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310
7311 /**
7312  * @class Roo.grid.AbstractSelectionModel
7313  * @extends Roo.util.Observable
7314  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7315  * implemented by descendant classes.  This class should not be directly instantiated.
7316  * @constructor
7317  */
7318 Roo.grid.AbstractSelectionModel = function(){
7319     this.locked = false;
7320     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7321 };
7322
7323 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7324     /** @ignore Called by the grid automatically. Do not call directly. */
7325     init : function(grid){
7326         this.grid = grid;
7327         this.initEvents();
7328     },
7329
7330     /**
7331      * Locks the selections.
7332      */
7333     lock : function(){
7334         this.locked = true;
7335     },
7336
7337     /**
7338      * Unlocks the selections.
7339      */
7340     unlock : function(){
7341         this.locked = false;
7342     },
7343
7344     /**
7345      * Returns true if the selections are locked.
7346      * @return {Boolean}
7347      */
7348     isLocked : function(){
7349         return this.locked;
7350     }
7351 });/*
7352  * Based on:
7353  * Ext JS Library 1.1.1
7354  * Copyright(c) 2006-2007, Ext JS, LLC.
7355  *
7356  * Originally Released Under LGPL - original licence link has changed is not relivant.
7357  *
7358  * Fork - LGPL
7359  * <script type="text/javascript">
7360  */
7361 /**
7362  * @extends Roo.grid.AbstractSelectionModel
7363  * @class Roo.grid.RowSelectionModel
7364  * The default SelectionModel used by {@link Roo.grid.Grid}.
7365  * It supports multiple selections and keyboard selection/navigation. 
7366  * @constructor
7367  * @param {Object} config
7368  */
7369 Roo.grid.RowSelectionModel = function(config){
7370     Roo.apply(this, config);
7371     this.selections = new Roo.util.MixedCollection(false, function(o){
7372         return o.id;
7373     });
7374
7375     this.last = false;
7376     this.lastActive = false;
7377
7378     this.addEvents({
7379         /**
7380         * @event selectionchange
7381         * Fires when the selection changes
7382         * @param {SelectionModel} this
7383         */
7384        "selectionchange" : true,
7385        /**
7386         * @event afterselectionchange
7387         * Fires after the selection changes (eg. by key press or clicking)
7388         * @param {SelectionModel} this
7389         */
7390        "afterselectionchange" : true,
7391        /**
7392         * @event beforerowselect
7393         * Fires when a row is selected being selected, return false to cancel.
7394         * @param {SelectionModel} this
7395         * @param {Number} rowIndex The selected index
7396         * @param {Boolean} keepExisting False if other selections will be cleared
7397         */
7398        "beforerowselect" : true,
7399        /**
7400         * @event rowselect
7401         * Fires when a row is selected.
7402         * @param {SelectionModel} this
7403         * @param {Number} rowIndex The selected index
7404         * @param {Roo.data.Record} r The record
7405         */
7406        "rowselect" : true,
7407        /**
7408         * @event rowdeselect
7409         * Fires when a row is deselected.
7410         * @param {SelectionModel} this
7411         * @param {Number} rowIndex The selected index
7412         */
7413         "rowdeselect" : true
7414     });
7415     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7416     this.locked = false;
7417 };
7418
7419 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7420     /**
7421      * @cfg {Boolean} singleSelect
7422      * True to allow selection of only one row at a time (defaults to false)
7423      */
7424     singleSelect : false,
7425
7426     // private
7427     initEvents : function(){
7428
7429         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7430             this.grid.on("mousedown", this.handleMouseDown, this);
7431         }else{ // allow click to work like normal
7432             this.grid.on("rowclick", this.handleDragableRowClick, this);
7433         }
7434         // bootstrap does not have a view..
7435         var view = this.grid.view ? this.grid.view : this.grid;
7436         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7437             "up" : function(e){
7438                 if(!e.shiftKey){
7439                     this.selectPrevious(e.shiftKey);
7440                 }else if(this.last !== false && this.lastActive !== false){
7441                     var last = this.last;
7442                     this.selectRange(this.last,  this.lastActive-1);
7443                     view.focusRow(this.lastActive);
7444                     if(last !== false){
7445                         this.last = last;
7446                     }
7447                 }else{
7448                     this.selectFirstRow();
7449                 }
7450                 this.fireEvent("afterselectionchange", this);
7451             },
7452             "down" : function(e){
7453                 if(!e.shiftKey){
7454                     this.selectNext(e.shiftKey);
7455                 }else if(this.last !== false && this.lastActive !== false){
7456                     var last = this.last;
7457                     this.selectRange(this.last,  this.lastActive+1);
7458                     view.focusRow(this.lastActive);
7459                     if(last !== false){
7460                         this.last = last;
7461                     }
7462                 }else{
7463                     this.selectFirstRow();
7464                 }
7465                 this.fireEvent("afterselectionchange", this);
7466             },
7467             scope: this
7468         });
7469
7470          
7471         view.on("refresh", this.onRefresh, this);
7472         view.on("rowupdated", this.onRowUpdated, this);
7473         view.on("rowremoved", this.onRemove, this);
7474     },
7475
7476     // private
7477     onRefresh : function(){
7478         var ds = this.grid.ds, i, v = this.grid.view;
7479         var s = this.selections;
7480         s.each(function(r){
7481             if((i = ds.indexOfId(r.id)) != -1){
7482                 v.onRowSelect(i);
7483                 s.add(ds.getAt(i)); // updating the selection relate data
7484             }else{
7485                 s.remove(r);
7486             }
7487         });
7488     },
7489
7490     // private
7491     onRemove : function(v, index, r){
7492         this.selections.remove(r);
7493     },
7494
7495     // private
7496     onRowUpdated : function(v, index, r){
7497         if(this.isSelected(r)){
7498             v.onRowSelect(index);
7499         }
7500     },
7501
7502     /**
7503      * Select records.
7504      * @param {Array} records The records to select
7505      * @param {Boolean} keepExisting (optional) True to keep existing selections
7506      */
7507     selectRecords : function(records, keepExisting){
7508         if(!keepExisting){
7509             this.clearSelections();
7510         }
7511         var ds = this.grid.ds;
7512         for(var i = 0, len = records.length; i < len; i++){
7513             this.selectRow(ds.indexOf(records[i]), true);
7514         }
7515     },
7516
7517     /**
7518      * Gets the number of selected rows.
7519      * @return {Number}
7520      */
7521     getCount : function(){
7522         return this.selections.length;
7523     },
7524
7525     /**
7526      * Selects the first row in the grid.
7527      */
7528     selectFirstRow : function(){
7529         this.selectRow(0);
7530     },
7531
7532     /**
7533      * Select the last row.
7534      * @param {Boolean} keepExisting (optional) True to keep existing selections
7535      */
7536     selectLastRow : function(keepExisting){
7537         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7538     },
7539
7540     /**
7541      * Selects the row immediately following the last selected row.
7542      * @param {Boolean} keepExisting (optional) True to keep existing selections
7543      */
7544     selectNext : function(keepExisting){
7545         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7546             this.selectRow(this.last+1, keepExisting);
7547             var view = this.grid.view ? this.grid.view : this.grid;
7548             view.focusRow(this.last);
7549         }
7550     },
7551
7552     /**
7553      * Selects the row that precedes the last selected row.
7554      * @param {Boolean} keepExisting (optional) True to keep existing selections
7555      */
7556     selectPrevious : function(keepExisting){
7557         if(this.last){
7558             this.selectRow(this.last-1, keepExisting);
7559             var view = this.grid.view ? this.grid.view : this.grid;
7560             view.focusRow(this.last);
7561         }
7562     },
7563
7564     /**
7565      * Returns the selected records
7566      * @return {Array} Array of selected records
7567      */
7568     getSelections : function(){
7569         return [].concat(this.selections.items);
7570     },
7571
7572     /**
7573      * Returns the first selected record.
7574      * @return {Record}
7575      */
7576     getSelected : function(){
7577         return this.selections.itemAt(0);
7578     },
7579
7580
7581     /**
7582      * Clears all selections.
7583      */
7584     clearSelections : function(fast){
7585         if(this.locked) {
7586             return;
7587         }
7588         if(fast !== true){
7589             var ds = this.grid.ds;
7590             var s = this.selections;
7591             s.each(function(r){
7592                 this.deselectRow(ds.indexOfId(r.id));
7593             }, this);
7594             s.clear();
7595         }else{
7596             this.selections.clear();
7597         }
7598         this.last = false;
7599     },
7600
7601
7602     /**
7603      * Selects all rows.
7604      */
7605     selectAll : function(){
7606         if(this.locked) {
7607             return;
7608         }
7609         this.selections.clear();
7610         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7611             this.selectRow(i, true);
7612         }
7613     },
7614
7615     /**
7616      * Returns True if there is a selection.
7617      * @return {Boolean}
7618      */
7619     hasSelection : function(){
7620         return this.selections.length > 0;
7621     },
7622
7623     /**
7624      * Returns True if the specified row is selected.
7625      * @param {Number/Record} record The record or index of the record to check
7626      * @return {Boolean}
7627      */
7628     isSelected : function(index){
7629         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7630         return (r && this.selections.key(r.id) ? true : false);
7631     },
7632
7633     /**
7634      * Returns True if the specified record id is selected.
7635      * @param {String} id The id of record to check
7636      * @return {Boolean}
7637      */
7638     isIdSelected : function(id){
7639         return (this.selections.key(id) ? true : false);
7640     },
7641
7642     // private
7643     handleMouseDown : function(e, t)
7644     {
7645         var view = this.grid.view ? this.grid.view : this.grid;
7646         var rowIndex;
7647         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7648             return;
7649         };
7650         if(e.shiftKey && this.last !== false){
7651             var last = this.last;
7652             this.selectRange(last, rowIndex, e.ctrlKey);
7653             this.last = last; // reset the last
7654             view.focusRow(rowIndex);
7655         }else{
7656             var isSelected = this.isSelected(rowIndex);
7657             if(e.button !== 0 && isSelected){
7658                 view.focusRow(rowIndex);
7659             }else if(e.ctrlKey && isSelected){
7660                 this.deselectRow(rowIndex);
7661             }else if(!isSelected){
7662                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7663                 view.focusRow(rowIndex);
7664             }
7665         }
7666         this.fireEvent("afterselectionchange", this);
7667     },
7668     // private
7669     handleDragableRowClick :  function(grid, rowIndex, e) 
7670     {
7671         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7672             this.selectRow(rowIndex, false);
7673             var view = this.grid.view ? this.grid.view : this.grid;
7674             view.focusRow(rowIndex);
7675              this.fireEvent("afterselectionchange", this);
7676         }
7677     },
7678     
7679     /**
7680      * Selects multiple rows.
7681      * @param {Array} rows Array of the indexes of the row to select
7682      * @param {Boolean} keepExisting (optional) True to keep existing selections
7683      */
7684     selectRows : function(rows, keepExisting){
7685         if(!keepExisting){
7686             this.clearSelections();
7687         }
7688         for(var i = 0, len = rows.length; i < len; i++){
7689             this.selectRow(rows[i], true);
7690         }
7691     },
7692
7693     /**
7694      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7695      * @param {Number} startRow The index of the first row in the range
7696      * @param {Number} endRow The index of the last row in the range
7697      * @param {Boolean} keepExisting (optional) True to retain existing selections
7698      */
7699     selectRange : function(startRow, endRow, keepExisting){
7700         if(this.locked) {
7701             return;
7702         }
7703         if(!keepExisting){
7704             this.clearSelections();
7705         }
7706         if(startRow <= endRow){
7707             for(var i = startRow; i <= endRow; i++){
7708                 this.selectRow(i, true);
7709             }
7710         }else{
7711             for(var i = startRow; i >= endRow; i--){
7712                 this.selectRow(i, true);
7713             }
7714         }
7715     },
7716
7717     /**
7718      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7719      * @param {Number} startRow The index of the first row in the range
7720      * @param {Number} endRow The index of the last row in the range
7721      */
7722     deselectRange : function(startRow, endRow, preventViewNotify){
7723         if(this.locked) {
7724             return;
7725         }
7726         for(var i = startRow; i <= endRow; i++){
7727             this.deselectRow(i, preventViewNotify);
7728         }
7729     },
7730
7731     /**
7732      * Selects a row.
7733      * @param {Number} row The index of the row to select
7734      * @param {Boolean} keepExisting (optional) True to keep existing selections
7735      */
7736     selectRow : function(index, keepExisting, preventViewNotify){
7737         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7738             return;
7739         }
7740         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7741             if(!keepExisting || this.singleSelect){
7742                 this.clearSelections();
7743             }
7744             var r = this.grid.ds.getAt(index);
7745             this.selections.add(r);
7746             this.last = this.lastActive = index;
7747             if(!preventViewNotify){
7748                 var view = this.grid.view ? this.grid.view : this.grid;
7749                 view.onRowSelect(index);
7750             }
7751             this.fireEvent("rowselect", this, index, r);
7752             this.fireEvent("selectionchange", this);
7753         }
7754     },
7755
7756     /**
7757      * Deselects a row.
7758      * @param {Number} row The index of the row to deselect
7759      */
7760     deselectRow : function(index, preventViewNotify){
7761         if(this.locked) {
7762             return;
7763         }
7764         if(this.last == index){
7765             this.last = false;
7766         }
7767         if(this.lastActive == index){
7768             this.lastActive = false;
7769         }
7770         var r = this.grid.ds.getAt(index);
7771         this.selections.remove(r);
7772         if(!preventViewNotify){
7773             var view = this.grid.view ? this.grid.view : this.grid;
7774             view.onRowDeselect(index);
7775         }
7776         this.fireEvent("rowdeselect", this, index);
7777         this.fireEvent("selectionchange", this);
7778     },
7779
7780     // private
7781     restoreLast : function(){
7782         if(this._last){
7783             this.last = this._last;
7784         }
7785     },
7786
7787     // private
7788     acceptsNav : function(row, col, cm){
7789         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7790     },
7791
7792     // private
7793     onEditorKey : function(field, e){
7794         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7795         if(k == e.TAB){
7796             e.stopEvent();
7797             ed.completeEdit();
7798             if(e.shiftKey){
7799                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7800             }else{
7801                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7802             }
7803         }else if(k == e.ENTER && !e.ctrlKey){
7804             e.stopEvent();
7805             ed.completeEdit();
7806             if(e.shiftKey){
7807                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7808             }else{
7809                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7810             }
7811         }else if(k == e.ESC){
7812             ed.cancelEdit();
7813         }
7814         if(newCell){
7815             g.startEditing(newCell[0], newCell[1]);
7816         }
7817     }
7818 });/*
7819  * Based on:
7820  * Ext JS Library 1.1.1
7821  * Copyright(c) 2006-2007, Ext JS, LLC.
7822  *
7823  * Originally Released Under LGPL - original licence link has changed is not relivant.
7824  *
7825  * Fork - LGPL
7826  * <script type="text/javascript">
7827  */
7828  
7829
7830 /**
7831  * @class Roo.grid.ColumnModel
7832  * @extends Roo.util.Observable
7833  * This is the default implementation of a ColumnModel used by the Grid. It defines
7834  * the columns in the grid.
7835  * <br>Usage:<br>
7836  <pre><code>
7837  var colModel = new Roo.grid.ColumnModel([
7838         {header: "Ticker", width: 60, sortable: true, locked: true},
7839         {header: "Company Name", width: 150, sortable: true},
7840         {header: "Market Cap.", width: 100, sortable: true},
7841         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7842         {header: "Employees", width: 100, sortable: true, resizable: false}
7843  ]);
7844  </code></pre>
7845  * <p>
7846  
7847  * The config options listed for this class are options which may appear in each
7848  * individual column definition.
7849  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7850  * @constructor
7851  * @param {Object} config An Array of column config objects. See this class's
7852  * config objects for details.
7853 */
7854 Roo.grid.ColumnModel = function(config){
7855         /**
7856      * The config passed into the constructor
7857      */
7858     this.config = []; //config;
7859     this.lookup = {};
7860
7861     // if no id, create one
7862     // if the column does not have a dataIndex mapping,
7863     // map it to the order it is in the config
7864     for(var i = 0, len = config.length; i < len; i++){
7865         this.addColumn(config[i]);
7866         
7867     }
7868
7869     /**
7870      * The width of columns which have no width specified (defaults to 100)
7871      * @type Number
7872      */
7873     this.defaultWidth = 100;
7874
7875     /**
7876      * Default sortable of columns which have no sortable specified (defaults to false)
7877      * @type Boolean
7878      */
7879     this.defaultSortable = false;
7880
7881     this.addEvents({
7882         /**
7883              * @event widthchange
7884              * Fires when the width of a column changes.
7885              * @param {ColumnModel} this
7886              * @param {Number} columnIndex The column index
7887              * @param {Number} newWidth The new width
7888              */
7889             "widthchange": true,
7890         /**
7891              * @event headerchange
7892              * Fires when the text of a header changes.
7893              * @param {ColumnModel} this
7894              * @param {Number} columnIndex The column index
7895              * @param {Number} newText The new header text
7896              */
7897             "headerchange": true,
7898         /**
7899              * @event hiddenchange
7900              * Fires when a column is hidden or "unhidden".
7901              * @param {ColumnModel} this
7902              * @param {Number} columnIndex The column index
7903              * @param {Boolean} hidden true if hidden, false otherwise
7904              */
7905             "hiddenchange": true,
7906             /**
7907          * @event columnmoved
7908          * Fires when a column is moved.
7909          * @param {ColumnModel} this
7910          * @param {Number} oldIndex
7911          * @param {Number} newIndex
7912          */
7913         "columnmoved" : true,
7914         /**
7915          * @event columlockchange
7916          * Fires when a column's locked state is changed
7917          * @param {ColumnModel} this
7918          * @param {Number} colIndex
7919          * @param {Boolean} locked true if locked
7920          */
7921         "columnlockchange" : true
7922     });
7923     Roo.grid.ColumnModel.superclass.constructor.call(this);
7924 };
7925 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7926     /**
7927      * @cfg {String} header The header text to display in the Grid view.
7928      */
7929     /**
7930      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7931      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7932      * specified, the column's index is used as an index into the Record's data Array.
7933      */
7934     /**
7935      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7936      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7937      */
7938     /**
7939      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7940      * Defaults to the value of the {@link #defaultSortable} property.
7941      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7942      */
7943     /**
7944      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7945      */
7946     /**
7947      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7948      */
7949     /**
7950      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7951      */
7952     /**
7953      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7954      */
7955     /**
7956      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7957      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7958      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7959      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7960      */
7961        /**
7962      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7963      */
7964     /**
7965      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7966      */
7967     /**
7968      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7969      */
7970     /**
7971      * @cfg {String} cursor (Optional)
7972      */
7973     /**
7974      * @cfg {String} tooltip (Optional)
7975      */
7976     /**
7977      * @cfg {Number} xs (Optional)
7978      */
7979     /**
7980      * @cfg {Number} sm (Optional)
7981      */
7982     /**
7983      * @cfg {Number} md (Optional)
7984      */
7985     /**
7986      * @cfg {Number} lg (Optional)
7987      */
7988     /**
7989      * Returns the id of the column at the specified index.
7990      * @param {Number} index The column index
7991      * @return {String} the id
7992      */
7993     getColumnId : function(index){
7994         return this.config[index].id;
7995     },
7996
7997     /**
7998      * Returns the column for a specified id.
7999      * @param {String} id The column id
8000      * @return {Object} the column
8001      */
8002     getColumnById : function(id){
8003         return this.lookup[id];
8004     },
8005
8006     
8007     /**
8008      * Returns the column Object for a specified dataIndex.
8009      * @param {String} dataIndex The column dataIndex
8010      * @return {Object|Boolean} the column or false if not found
8011      */
8012     getColumnByDataIndex: function(dataIndex){
8013         var index = this.findColumnIndex(dataIndex);
8014         return index > -1 ? this.config[index] : false;
8015     },
8016     
8017     /**
8018      * Returns the index for a specified column id.
8019      * @param {String} id The column id
8020      * @return {Number} the index, or -1 if not found
8021      */
8022     getIndexById : function(id){
8023         for(var i = 0, len = this.config.length; i < len; i++){
8024             if(this.config[i].id == id){
8025                 return i;
8026             }
8027         }
8028         return -1;
8029     },
8030     
8031     /**
8032      * Returns the index for a specified column dataIndex.
8033      * @param {String} dataIndex The column dataIndex
8034      * @return {Number} the index, or -1 if not found
8035      */
8036     
8037     findColumnIndex : function(dataIndex){
8038         for(var i = 0, len = this.config.length; i < len; i++){
8039             if(this.config[i].dataIndex == dataIndex){
8040                 return i;
8041             }
8042         }
8043         return -1;
8044     },
8045     
8046     
8047     moveColumn : function(oldIndex, newIndex){
8048         var c = this.config[oldIndex];
8049         this.config.splice(oldIndex, 1);
8050         this.config.splice(newIndex, 0, c);
8051         this.dataMap = null;
8052         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8053     },
8054
8055     isLocked : function(colIndex){
8056         return this.config[colIndex].locked === true;
8057     },
8058
8059     setLocked : function(colIndex, value, suppressEvent){
8060         if(this.isLocked(colIndex) == value){
8061             return;
8062         }
8063         this.config[colIndex].locked = value;
8064         if(!suppressEvent){
8065             this.fireEvent("columnlockchange", this, colIndex, value);
8066         }
8067     },
8068
8069     getTotalLockedWidth : function(){
8070         var totalWidth = 0;
8071         for(var i = 0; i < this.config.length; i++){
8072             if(this.isLocked(i) && !this.isHidden(i)){
8073                 this.totalWidth += this.getColumnWidth(i);
8074             }
8075         }
8076         return totalWidth;
8077     },
8078
8079     getLockedCount : function(){
8080         for(var i = 0, len = this.config.length; i < len; i++){
8081             if(!this.isLocked(i)){
8082                 return i;
8083             }
8084         }
8085         
8086         return this.config.length;
8087     },
8088
8089     /**
8090      * Returns the number of columns.
8091      * @return {Number}
8092      */
8093     getColumnCount : function(visibleOnly){
8094         if(visibleOnly === true){
8095             var c = 0;
8096             for(var i = 0, len = this.config.length; i < len; i++){
8097                 if(!this.isHidden(i)){
8098                     c++;
8099                 }
8100             }
8101             return c;
8102         }
8103         return this.config.length;
8104     },
8105
8106     /**
8107      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8108      * @param {Function} fn
8109      * @param {Object} scope (optional)
8110      * @return {Array} result
8111      */
8112     getColumnsBy : function(fn, scope){
8113         var r = [];
8114         for(var i = 0, len = this.config.length; i < len; i++){
8115             var c = this.config[i];
8116             if(fn.call(scope||this, c, i) === true){
8117                 r[r.length] = c;
8118             }
8119         }
8120         return r;
8121     },
8122
8123     /**
8124      * Returns true if the specified column is sortable.
8125      * @param {Number} col The column index
8126      * @return {Boolean}
8127      */
8128     isSortable : function(col){
8129         if(typeof this.config[col].sortable == "undefined"){
8130             return this.defaultSortable;
8131         }
8132         return this.config[col].sortable;
8133     },
8134
8135     /**
8136      * Returns the rendering (formatting) function defined for the column.
8137      * @param {Number} col The column index.
8138      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8139      */
8140     getRenderer : function(col){
8141         if(!this.config[col].renderer){
8142             return Roo.grid.ColumnModel.defaultRenderer;
8143         }
8144         return this.config[col].renderer;
8145     },
8146
8147     /**
8148      * Sets the rendering (formatting) function for a column.
8149      * @param {Number} col The column index
8150      * @param {Function} fn The function to use to process the cell's raw data
8151      * to return HTML markup for the grid view. The render function is called with
8152      * the following parameters:<ul>
8153      * <li>Data value.</li>
8154      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8155      * <li>css A CSS style string to apply to the table cell.</li>
8156      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8157      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8158      * <li>Row index</li>
8159      * <li>Column index</li>
8160      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8161      */
8162     setRenderer : function(col, fn){
8163         this.config[col].renderer = fn;
8164     },
8165
8166     /**
8167      * Returns the width for the specified column.
8168      * @param {Number} col The column index
8169      * @return {Number}
8170      */
8171     getColumnWidth : function(col){
8172         return this.config[col].width * 1 || this.defaultWidth;
8173     },
8174
8175     /**
8176      * Sets the width for a column.
8177      * @param {Number} col The column index
8178      * @param {Number} width The new width
8179      */
8180     setColumnWidth : function(col, width, suppressEvent){
8181         this.config[col].width = width;
8182         this.totalWidth = null;
8183         if(!suppressEvent){
8184              this.fireEvent("widthchange", this, col, width);
8185         }
8186     },
8187
8188     /**
8189      * Returns the total width of all columns.
8190      * @param {Boolean} includeHidden True to include hidden column widths
8191      * @return {Number}
8192      */
8193     getTotalWidth : function(includeHidden){
8194         if(!this.totalWidth){
8195             this.totalWidth = 0;
8196             for(var i = 0, len = this.config.length; i < len; i++){
8197                 if(includeHidden || !this.isHidden(i)){
8198                     this.totalWidth += this.getColumnWidth(i);
8199                 }
8200             }
8201         }
8202         return this.totalWidth;
8203     },
8204
8205     /**
8206      * Returns the header for the specified column.
8207      * @param {Number} col The column index
8208      * @return {String}
8209      */
8210     getColumnHeader : function(col){
8211         return this.config[col].header;
8212     },
8213
8214     /**
8215      * Sets the header for a column.
8216      * @param {Number} col The column index
8217      * @param {String} header The new header
8218      */
8219     setColumnHeader : function(col, header){
8220         this.config[col].header = header;
8221         this.fireEvent("headerchange", this, col, header);
8222     },
8223
8224     /**
8225      * Returns the tooltip for the specified column.
8226      * @param {Number} col The column index
8227      * @return {String}
8228      */
8229     getColumnTooltip : function(col){
8230             return this.config[col].tooltip;
8231     },
8232     /**
8233      * Sets the tooltip for a column.
8234      * @param {Number} col The column index
8235      * @param {String} tooltip The new tooltip
8236      */
8237     setColumnTooltip : function(col, tooltip){
8238             this.config[col].tooltip = tooltip;
8239     },
8240
8241     /**
8242      * Returns the dataIndex for the specified column.
8243      * @param {Number} col The column index
8244      * @return {Number}
8245      */
8246     getDataIndex : function(col){
8247         return this.config[col].dataIndex;
8248     },
8249
8250     /**
8251      * Sets the dataIndex for a column.
8252      * @param {Number} col The column index
8253      * @param {Number} dataIndex The new dataIndex
8254      */
8255     setDataIndex : function(col, dataIndex){
8256         this.config[col].dataIndex = dataIndex;
8257     },
8258
8259     
8260     
8261     /**
8262      * Returns true if the cell is editable.
8263      * @param {Number} colIndex The column index
8264      * @param {Number} rowIndex The row index - this is nto actually used..?
8265      * @return {Boolean}
8266      */
8267     isCellEditable : function(colIndex, rowIndex){
8268         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8269     },
8270
8271     /**
8272      * Returns the editor defined for the cell/column.
8273      * return false or null to disable editing.
8274      * @param {Number} colIndex The column index
8275      * @param {Number} rowIndex The row index
8276      * @return {Object}
8277      */
8278     getCellEditor : function(colIndex, rowIndex){
8279         return this.config[colIndex].editor;
8280     },
8281
8282     /**
8283      * Sets if a column is editable.
8284      * @param {Number} col The column index
8285      * @param {Boolean} editable True if the column is editable
8286      */
8287     setEditable : function(col, editable){
8288         this.config[col].editable = editable;
8289     },
8290
8291
8292     /**
8293      * Returns true if the column is hidden.
8294      * @param {Number} colIndex The column index
8295      * @return {Boolean}
8296      */
8297     isHidden : function(colIndex){
8298         return this.config[colIndex].hidden;
8299     },
8300
8301
8302     /**
8303      * Returns true if the column width cannot be changed
8304      */
8305     isFixed : function(colIndex){
8306         return this.config[colIndex].fixed;
8307     },
8308
8309     /**
8310      * Returns true if the column can be resized
8311      * @return {Boolean}
8312      */
8313     isResizable : function(colIndex){
8314         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8315     },
8316     /**
8317      * Sets if a column is hidden.
8318      * @param {Number} colIndex The column index
8319      * @param {Boolean} hidden True if the column is hidden
8320      */
8321     setHidden : function(colIndex, hidden){
8322         this.config[colIndex].hidden = hidden;
8323         this.totalWidth = null;
8324         this.fireEvent("hiddenchange", this, colIndex, hidden);
8325     },
8326
8327     /**
8328      * Sets the editor for a column.
8329      * @param {Number} col The column index
8330      * @param {Object} editor The editor object
8331      */
8332     setEditor : function(col, editor){
8333         this.config[col].editor = editor;
8334     },
8335     /**
8336      * Add a column (experimental...) - defaults to adding to the end..
8337      * @param {Object} config 
8338     */
8339     addColumn : function(c)
8340     {
8341     
8342         var i = this.config.length;
8343         this.config[i] = c;
8344         
8345         if(typeof c.dataIndex == "undefined"){
8346             c.dataIndex = i;
8347         }
8348         if(typeof c.renderer == "string"){
8349             c.renderer = Roo.util.Format[c.renderer];
8350         }
8351         if(typeof c.id == "undefined"){
8352             c.id = Roo.id();
8353         }
8354         if(c.editor && c.editor.xtype){
8355             c.editor  = Roo.factory(c.editor, Roo.grid);
8356         }
8357         if(c.editor && c.editor.isFormField){
8358             c.editor = new Roo.grid.GridEditor(c.editor);
8359         }
8360         this.lookup[c.id] = c;
8361     }
8362     
8363 });
8364
8365 Roo.grid.ColumnModel.defaultRenderer = function(value)
8366 {
8367     if(typeof value == "object") {
8368         return value;
8369     }
8370         if(typeof value == "string" && value.length < 1){
8371             return "&#160;";
8372         }
8373     
8374         return String.format("{0}", value);
8375 };
8376
8377 // Alias for backwards compatibility
8378 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8379 /*
8380  * Based on:
8381  * Ext JS Library 1.1.1
8382  * Copyright(c) 2006-2007, Ext JS, LLC.
8383  *
8384  * Originally Released Under LGPL - original licence link has changed is not relivant.
8385  *
8386  * Fork - LGPL
8387  * <script type="text/javascript">
8388  */
8389  
8390 /**
8391  * @class Roo.LoadMask
8392  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8393  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8394  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8395  * element's UpdateManager load indicator and will be destroyed after the initial load.
8396  * @constructor
8397  * Create a new LoadMask
8398  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8399  * @param {Object} config The config object
8400  */
8401 Roo.LoadMask = function(el, config){
8402     this.el = Roo.get(el);
8403     Roo.apply(this, config);
8404     if(this.store){
8405         this.store.on('beforeload', this.onBeforeLoad, this);
8406         this.store.on('load', this.onLoad, this);
8407         this.store.on('loadexception', this.onLoadException, this);
8408         this.removeMask = false;
8409     }else{
8410         var um = this.el.getUpdateManager();
8411         um.showLoadIndicator = false; // disable the default indicator
8412         um.on('beforeupdate', this.onBeforeLoad, this);
8413         um.on('update', this.onLoad, this);
8414         um.on('failure', this.onLoad, this);
8415         this.removeMask = true;
8416     }
8417 };
8418
8419 Roo.LoadMask.prototype = {
8420     /**
8421      * @cfg {Boolean} removeMask
8422      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8423      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8424      */
8425     /**
8426      * @cfg {String} msg
8427      * The text to display in a centered loading message box (defaults to 'Loading...')
8428      */
8429     msg : 'Loading...',
8430     /**
8431      * @cfg {String} msgCls
8432      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8433      */
8434     msgCls : 'x-mask-loading',
8435
8436     /**
8437      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8438      * @type Boolean
8439      */
8440     disabled: false,
8441
8442     /**
8443      * Disables the mask to prevent it from being displayed
8444      */
8445     disable : function(){
8446        this.disabled = true;
8447     },
8448
8449     /**
8450      * Enables the mask so that it can be displayed
8451      */
8452     enable : function(){
8453         this.disabled = false;
8454     },
8455     
8456     onLoadException : function()
8457     {
8458         Roo.log(arguments);
8459         
8460         if (typeof(arguments[3]) != 'undefined') {
8461             Roo.MessageBox.alert("Error loading",arguments[3]);
8462         } 
8463         /*
8464         try {
8465             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8466                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8467             }   
8468         } catch(e) {
8469             
8470         }
8471         */
8472     
8473         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8474     },
8475     // private
8476     onLoad : function()
8477     {
8478         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8479     },
8480
8481     // private
8482     onBeforeLoad : function(){
8483         if(!this.disabled){
8484             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8485         }
8486     },
8487
8488     // private
8489     destroy : function(){
8490         if(this.store){
8491             this.store.un('beforeload', this.onBeforeLoad, this);
8492             this.store.un('load', this.onLoad, this);
8493             this.store.un('loadexception', this.onLoadException, this);
8494         }else{
8495             var um = this.el.getUpdateManager();
8496             um.un('beforeupdate', this.onBeforeLoad, this);
8497             um.un('update', this.onLoad, this);
8498             um.un('failure', this.onLoad, this);
8499         }
8500     }
8501 };/**
8502  * @class Roo.bootstrap.Table
8503  * @licence LGBL
8504  * @extends Roo.bootstrap.Component
8505  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8506  * Similar to Roo.grid.Grid
8507  * <pre><code>
8508  var table = Roo.factory({
8509     xtype : 'Table',
8510     xns : Roo.bootstrap,
8511     autoSizeColumns: true,
8512     
8513     
8514     store : {
8515         xtype : 'Store',
8516         xns : Roo.data,
8517         remoteSort : true,
8518         sortInfo : { direction : 'ASC', field: 'name' },
8519         proxy : {
8520            xtype : 'HttpProxy',
8521            xns : Roo.data,
8522            method : 'GET',
8523            url : 'https://example.com/some.data.url.json'
8524         },
8525         reader : {
8526            xtype : 'JsonReader',
8527            xns : Roo.data,
8528            fields : [ 'id', 'name', whatever' ],
8529            id : 'id',
8530            root : 'data'
8531         }
8532     },
8533     cm : [
8534         {
8535             xtype : 'ColumnModel',
8536             xns : Roo.grid,
8537             align : 'center',
8538             cursor : 'pointer',
8539             dataIndex : 'is_in_group',
8540             header : "Name",
8541             sortable : true,
8542             renderer : function(v, x , r) {  
8543             
8544                 return String.format("{0}", v)
8545             }
8546             width : 3
8547         } // more columns..
8548     ],
8549     selModel : {
8550         xtype : 'RowSelectionModel',
8551         xns : Roo.bootstrap.Table
8552         // you can add listeners to catch selection change here....
8553     }
8554      
8555
8556  });
8557  // set any options
8558  grid.render(Roo.get("some-div"));
8559 </code></pre>
8560
8561 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8562
8563
8564
8565  *
8566  * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8567  * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8568  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8569  * 
8570  * @cfg {String} cls table class
8571  *
8572  * 
8573  * @cfg {boolean} striped Should the rows be alternative striped
8574  * @cfg {boolean} bordered Add borders to the table
8575  * @cfg {boolean} hover Add hover highlighting
8576  * @cfg {boolean} condensed Format condensed
8577  * @cfg {boolean} responsive Format condensed
8578  * @cfg {Boolean} loadMask (true|false) default false
8579  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8580  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8581  * @cfg {Boolean} rowSelection (true|false) default false
8582  * @cfg {Boolean} cellSelection (true|false) default false
8583  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8584  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8585  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8586  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8587  
8588  * 
8589  * @constructor
8590  * Create a new Table
8591  * @param {Object} config The config object
8592  */
8593
8594 Roo.bootstrap.Table = function(config)
8595 {
8596     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8597      
8598     // BC...
8599     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8600     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8601     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8602     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8603     
8604     this.view = this; // compat with grid.
8605     
8606     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8607     if (this.sm) {
8608         this.sm.grid = this;
8609         this.selModel = Roo.factory(this.sm, Roo.grid);
8610         this.sm = this.selModel;
8611         this.sm.xmodule = this.xmodule || false;
8612     }
8613     
8614     if (this.cm && typeof(this.cm.config) == 'undefined') {
8615         this.colModel = new Roo.grid.ColumnModel(this.cm);
8616         this.cm = this.colModel;
8617         this.cm.xmodule = this.xmodule || false;
8618     }
8619     if (this.store) {
8620         this.store= Roo.factory(this.store, Roo.data);
8621         this.ds = this.store;
8622         this.ds.xmodule = this.xmodule || false;
8623          
8624     }
8625     if (this.footer && this.store) {
8626         this.footer.dataSource = this.ds;
8627         this.footer = Roo.factory(this.footer);
8628     }
8629     
8630     /** @private */
8631     this.addEvents({
8632         /**
8633          * @event cellclick
8634          * Fires when a cell is clicked
8635          * @param {Roo.bootstrap.Table} this
8636          * @param {Roo.Element} el
8637          * @param {Number} rowIndex
8638          * @param {Number} columnIndex
8639          * @param {Roo.EventObject} e
8640          */
8641         "cellclick" : true,
8642         /**
8643          * @event celldblclick
8644          * Fires when a cell is double clicked
8645          * @param {Roo.bootstrap.Table} this
8646          * @param {Roo.Element} el
8647          * @param {Number} rowIndex
8648          * @param {Number} columnIndex
8649          * @param {Roo.EventObject} e
8650          */
8651         "celldblclick" : true,
8652         /**
8653          * @event rowclick
8654          * Fires when a row is clicked
8655          * @param {Roo.bootstrap.Table} this
8656          * @param {Roo.Element} el
8657          * @param {Number} rowIndex
8658          * @param {Roo.EventObject} e
8659          */
8660         "rowclick" : true,
8661         /**
8662          * @event rowdblclick
8663          * Fires when a row is double clicked
8664          * @param {Roo.bootstrap.Table} this
8665          * @param {Roo.Element} el
8666          * @param {Number} rowIndex
8667          * @param {Roo.EventObject} e
8668          */
8669         "rowdblclick" : true,
8670         /**
8671          * @event mouseover
8672          * Fires when a mouseover occur
8673          * @param {Roo.bootstrap.Table} this
8674          * @param {Roo.Element} el
8675          * @param {Number} rowIndex
8676          * @param {Number} columnIndex
8677          * @param {Roo.EventObject} e
8678          */
8679         "mouseover" : true,
8680         /**
8681          * @event mouseout
8682          * Fires when a mouseout occur
8683          * @param {Roo.bootstrap.Table} this
8684          * @param {Roo.Element} el
8685          * @param {Number} rowIndex
8686          * @param {Number} columnIndex
8687          * @param {Roo.EventObject} e
8688          */
8689         "mouseout" : true,
8690         /**
8691          * @event rowclass
8692          * Fires when a row is rendered, so you can change add a style to it.
8693          * @param {Roo.bootstrap.Table} this
8694          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8695          */
8696         'rowclass' : true,
8697           /**
8698          * @event rowsrendered
8699          * Fires when all the  rows have been rendered
8700          * @param {Roo.bootstrap.Table} this
8701          */
8702         'rowsrendered' : true,
8703         /**
8704          * @event contextmenu
8705          * The raw contextmenu event for the entire grid.
8706          * @param {Roo.EventObject} e
8707          */
8708         "contextmenu" : true,
8709         /**
8710          * @event rowcontextmenu
8711          * Fires when a row is right clicked
8712          * @param {Roo.bootstrap.Table} this
8713          * @param {Number} rowIndex
8714          * @param {Roo.EventObject} e
8715          */
8716         "rowcontextmenu" : true,
8717         /**
8718          * @event cellcontextmenu
8719          * Fires when a cell is right clicked
8720          * @param {Roo.bootstrap.Table} this
8721          * @param {Number} rowIndex
8722          * @param {Number} cellIndex
8723          * @param {Roo.EventObject} e
8724          */
8725          "cellcontextmenu" : true,
8726          /**
8727          * @event headercontextmenu
8728          * Fires when a header is right clicked
8729          * @param {Roo.bootstrap.Table} this
8730          * @param {Number} columnIndex
8731          * @param {Roo.EventObject} e
8732          */
8733         "headercontextmenu" : true,
8734         /**
8735          * @event mousedown
8736          * The raw mousedown event for the entire grid.
8737          * @param {Roo.EventObject} e
8738          */
8739         "mousedown" : true
8740         
8741     });
8742 };
8743
8744 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8745     
8746     cls: false,
8747     
8748     striped : false,
8749     scrollBody : false,
8750     bordered: false,
8751     hover:  false,
8752     condensed : false,
8753     responsive : false,
8754     sm : false,
8755     cm : false,
8756     store : false,
8757     loadMask : false,
8758     footerShow : true,
8759     headerShow : true,
8760   
8761     rowSelection : false,
8762     cellSelection : false,
8763     layout : false,
8764     
8765     // Roo.Element - the tbody
8766     bodyEl: false,  // <tbody> Roo.Element - thead element
8767     
8768     headEl: false,  // <thead> Roo.Element - thead element
8769     
8770     container: false, // used by gridpanel...
8771     
8772     lazyLoad : false,
8773     
8774     CSS : Roo.util.CSS,
8775     
8776     auto_hide_footer : false,
8777     
8778     view: false, // actually points to this..
8779     
8780     getAutoCreate : function()
8781     {
8782         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8783         
8784         cfg = {
8785             tag: 'table',
8786             cls : 'table', 
8787             cn : []
8788         };
8789         // this get's auto added by panel.Grid
8790         if (this.scrollBody) {
8791             cfg.cls += ' table-body-fixed';
8792         }    
8793         if (this.striped) {
8794             cfg.cls += ' table-striped';
8795         }
8796         
8797         if (this.hover) {
8798             cfg.cls += ' table-hover';
8799         }
8800         if (this.bordered) {
8801             cfg.cls += ' table-bordered';
8802         }
8803         if (this.condensed) {
8804             cfg.cls += ' table-condensed';
8805         }
8806         
8807         if (this.responsive) {
8808             cfg.cls += ' table-responsive';
8809         }
8810         
8811         if (this.cls) {
8812             cfg.cls+=  ' ' +this.cls;
8813         }
8814         
8815         
8816         
8817         if (this.layout) {
8818             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8819         }
8820         
8821         if(this.store || this.cm){
8822             if(this.headerShow){
8823                 cfg.cn.push(this.renderHeader());
8824             }
8825             
8826             cfg.cn.push(this.renderBody());
8827             
8828             if(this.footerShow){
8829                 cfg.cn.push(this.renderFooter());
8830             }
8831             // where does this come from?
8832             //cfg.cls+=  ' TableGrid';
8833         }
8834         
8835         return { cn : [ cfg ] };
8836     },
8837     
8838     initEvents : function()
8839     {   
8840         if(!this.store || !this.cm){
8841             return;
8842         }
8843         if (this.selModel) {
8844             this.selModel.initEvents();
8845         }
8846         
8847         
8848         //Roo.log('initEvents with ds!!!!');
8849         
8850         this.bodyEl = this.el.select('tbody', true).first();
8851         this.headEl = this.el.select('thead', true).first();
8852         this.mainFoot = this.el.select('tfoot', true).first();
8853         
8854         
8855         
8856         
8857         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8858             e.on('click', this.sort, this);
8859         }, this);
8860         
8861         
8862         // why is this done????? = it breaks dialogs??
8863         //this.parent().el.setStyle('position', 'relative');
8864         
8865         
8866         if (this.footer) {
8867             this.footer.parentId = this.id;
8868             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8869             
8870             if(this.lazyLoad){
8871                 this.el.select('tfoot tr td').first().addClass('hide');
8872             }
8873         } 
8874         
8875         if(this.loadMask) {
8876             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8877         }
8878         
8879         this.store.on('load', this.onLoad, this);
8880         this.store.on('beforeload', this.onBeforeLoad, this);
8881         this.store.on('update', this.onUpdate, this);
8882         this.store.on('add', this.onAdd, this);
8883         this.store.on("clear", this.clear, this);
8884         
8885         this.el.on("contextmenu", this.onContextMenu, this);
8886         
8887         
8888         this.cm.on("headerchange", this.onHeaderChange, this);
8889         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8890
8891  //?? does bodyEl get replaced on render?
8892         this.bodyEl.on("click", this.onClick, this);
8893         this.bodyEl.on("dblclick", this.onDblClick, this);        
8894         this.bodyEl.on('scroll', this.onBodyScroll, this);
8895
8896         // guessing mainbody will work - this relays usually caught by selmodel at present.
8897         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
8898   
8899         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
8900             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
8901         }
8902         
8903         
8904     },
8905     // Compatibility with grid - we implement all the view features at present.
8906     getView : function()
8907     {
8908         return this;
8909     },
8910     
8911     onContextMenu : function(e, t)
8912     {
8913         this.processEvent("contextmenu", e);
8914     },
8915     
8916     processEvent : function(name, e)
8917     {
8918         if (name != 'touchstart' ) {
8919             this.fireEvent(name, e);    
8920         }
8921         
8922         var t = e.getTarget();
8923         
8924         var cell = Roo.get(t);
8925         
8926         if(!cell){
8927             return;
8928         }
8929         
8930         if(cell.findParent('tfoot', false, true)){
8931             return;
8932         }
8933         
8934         if(cell.findParent('thead', false, true)){
8935             
8936             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8937                 cell = Roo.get(t).findParent('th', false, true);
8938                 if (!cell) {
8939                     Roo.log("failed to find th in thead?");
8940                     Roo.log(e.getTarget());
8941                     return;
8942                 }
8943             }
8944             
8945             var cellIndex = cell.dom.cellIndex;
8946             
8947             var ename = name == 'touchstart' ? 'click' : name;
8948             this.fireEvent("header" + ename, this, cellIndex, e);
8949             
8950             return;
8951         }
8952         
8953         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8954             cell = Roo.get(t).findParent('td', false, true);
8955             if (!cell) {
8956                 Roo.log("failed to find th in tbody?");
8957                 Roo.log(e.getTarget());
8958                 return;
8959             }
8960         }
8961         
8962         var row = cell.findParent('tr', false, true);
8963         var cellIndex = cell.dom.cellIndex;
8964         var rowIndex = row.dom.rowIndex - 1;
8965         
8966         if(row !== false){
8967             
8968             this.fireEvent("row" + name, this, rowIndex, e);
8969             
8970             if(cell !== false){
8971             
8972                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8973             }
8974         }
8975         
8976     },
8977     
8978     onMouseover : function(e, el)
8979     {
8980         var cell = Roo.get(el);
8981         
8982         if(!cell){
8983             return;
8984         }
8985         
8986         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8987             cell = cell.findParent('td', false, true);
8988         }
8989         
8990         var row = cell.findParent('tr', false, true);
8991         var cellIndex = cell.dom.cellIndex;
8992         var rowIndex = row.dom.rowIndex - 1; // start from 0
8993         
8994         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8995         
8996     },
8997     
8998     onMouseout : function(e, el)
8999     {
9000         var cell = Roo.get(el);
9001         
9002         if(!cell){
9003             return;
9004         }
9005         
9006         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9007             cell = cell.findParent('td', false, true);
9008         }
9009         
9010         var row = cell.findParent('tr', false, true);
9011         var cellIndex = cell.dom.cellIndex;
9012         var rowIndex = row.dom.rowIndex - 1; // start from 0
9013         
9014         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9015         
9016     },
9017     
9018     onClick : function(e, el)
9019     {
9020         var cell = Roo.get(el);
9021         
9022         if(!cell || (!this.cellSelection && !this.rowSelection)){
9023             return;
9024         }
9025         
9026         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9027             cell = cell.findParent('td', false, true);
9028         }
9029         
9030         if(!cell || typeof(cell) == 'undefined'){
9031             return;
9032         }
9033         
9034         var row = cell.findParent('tr', false, true);
9035         
9036         if(!row || typeof(row) == 'undefined'){
9037             return;
9038         }
9039         
9040         var cellIndex = cell.dom.cellIndex;
9041         var rowIndex = this.getRowIndex(row);
9042         
9043         // why??? - should these not be based on SelectionModel?
9044         //if(this.cellSelection){
9045             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9046         //}
9047         
9048         //if(this.rowSelection){
9049             this.fireEvent('rowclick', this, row, rowIndex, e);
9050         //}
9051          
9052     },
9053         
9054     onDblClick : function(e,el)
9055     {
9056         var cell = Roo.get(el);
9057         
9058         if(!cell || (!this.cellSelection && !this.rowSelection)){
9059             return;
9060         }
9061         
9062         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9063             cell = cell.findParent('td', false, true);
9064         }
9065         
9066         if(!cell || typeof(cell) == 'undefined'){
9067             return;
9068         }
9069         
9070         var row = cell.findParent('tr', false, true);
9071         
9072         if(!row || typeof(row) == 'undefined'){
9073             return;
9074         }
9075         
9076         var cellIndex = cell.dom.cellIndex;
9077         var rowIndex = this.getRowIndex(row);
9078         
9079         if(this.cellSelection){
9080             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9081         }
9082         
9083         if(this.rowSelection){
9084             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9085         }
9086     },
9087     findRowIndex : function(el)
9088     {
9089         var cell = Roo.get(el);
9090         if(!cell) {
9091             return false;
9092         }
9093         var row = cell.findParent('tr', false, true);
9094         
9095         if(!row || typeof(row) == 'undefined'){
9096             return false;
9097         }
9098         return this.getRowIndex(row);
9099     },
9100     sort : function(e,el)
9101     {
9102         var col = Roo.get(el);
9103         
9104         if(!col.hasClass('sortable')){
9105             return;
9106         }
9107         
9108         var sort = col.attr('sort');
9109         var dir = 'ASC';
9110         
9111         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9112             dir = 'DESC';
9113         }
9114         
9115         this.store.sortInfo = {field : sort, direction : dir};
9116         
9117         if (this.footer) {
9118             Roo.log("calling footer first");
9119             this.footer.onClick('first');
9120         } else {
9121         
9122             this.store.load({ params : { start : 0 } });
9123         }
9124     },
9125     
9126     renderHeader : function()
9127     {
9128         var header = {
9129             tag: 'thead',
9130             cn : []
9131         };
9132         
9133         var cm = this.cm;
9134         this.totalWidth = 0;
9135         
9136         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9137             
9138             var config = cm.config[i];
9139             
9140             var c = {
9141                 tag: 'th',
9142                 cls : 'x-hcol-' + i,
9143                 style : '',
9144                 
9145                 html: cm.getColumnHeader(i)
9146             };
9147             
9148             var tooltip = cm.getColumnTooltip(i);
9149             if (tooltip) {
9150                 c.tooltip = tooltip;
9151             }
9152             
9153             
9154             var hh = '';
9155             
9156             if(typeof(config.sortable) != 'undefined' && config.sortable){
9157                 c.cls = 'sortable';
9158                 c.html = '<i class="fa"></i>' + c.html;
9159             }
9160             
9161             // could use BS4 hidden-..-down 
9162             
9163             if(typeof(config.lgHeader) != 'undefined'){
9164                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9165             }
9166             
9167             if(typeof(config.mdHeader) != 'undefined'){
9168                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9169             }
9170             
9171             if(typeof(config.smHeader) != 'undefined'){
9172                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9173             }
9174             
9175             if(typeof(config.xsHeader) != 'undefined'){
9176                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9177             }
9178             
9179             if(hh.length){
9180                 c.html = hh;
9181             }
9182             
9183             if(typeof(config.tooltip) != 'undefined'){
9184                 c.tooltip = config.tooltip;
9185             }
9186             
9187             if(typeof(config.colspan) != 'undefined'){
9188                 c.colspan = config.colspan;
9189             }
9190             
9191             if(typeof(config.hidden) != 'undefined' && config.hidden){
9192                 c.cls += ' d-none';
9193             } else {
9194                 c.cls += ' d-block';
9195             }
9196             
9197             if(typeof(config.dataIndex) != 'undefined'){
9198                 c.sort = config.dataIndex;
9199             }
9200             
9201            
9202             
9203             if(typeof(config.align) != 'undefined' && config.align.length){
9204                 c.style += ' text-align:' + config.align + ';';
9205             }
9206             
9207             if(typeof(config.width) != 'undefined'){
9208                 c.style += ' width:' + config.width + 'px;';
9209                 this.totalWidth += config.width;
9210             } else {
9211                 this.totalWidth += 100; // assume minimum of 100 per column?
9212             }
9213             
9214             if(typeof(config.cls) != 'undefined'){
9215                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9216             }
9217             
9218             ['xs','sm','md','lg'].map(function(size){
9219                 
9220                 if(typeof(config[size]) == 'undefined'){
9221                     return;
9222                 }
9223                  
9224                 if (!config[size]) { // 0 = hidden
9225                     // BS 4 '0' is treated as hide that column and below.
9226                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9227                     return;
9228                 }
9229                 
9230                 c.cls += ' col-' + size + '-' + config[size] + (
9231                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9232                 );
9233                 
9234                 
9235             });
9236             // at the end?
9237             c.html +=' <span class="roo-hd-split"></span>';
9238             
9239             
9240             header.cn.push(c)
9241         }
9242         
9243         return header;
9244     },
9245     
9246     renderBody : function()
9247     {
9248         var body = {
9249             tag: 'tbody',
9250             cn : [
9251                 {
9252                     tag: 'tr',
9253                     cn : [
9254                         {
9255                             tag : 'td',
9256                             colspan :  this.cm.getColumnCount()
9257                         }
9258                     ]
9259                 }
9260             ]
9261         };
9262         
9263         return body;
9264     },
9265     
9266     renderFooter : function()
9267     {
9268         var footer = {
9269             tag: 'tfoot',
9270             cn : [
9271                 {
9272                     tag: 'tr',
9273                     cn : [
9274                         {
9275                             tag : 'td',
9276                             colspan :  this.cm.getColumnCount()
9277                         }
9278                     ]
9279                 }
9280             ]
9281         };
9282         
9283         return footer;
9284     },
9285     
9286     
9287     
9288     onLoad : function()
9289     {
9290 //        Roo.log('ds onload');
9291         this.clear();
9292         
9293         var _this = this;
9294         var cm = this.cm;
9295         var ds = this.store;
9296         
9297         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9298             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9299             if (_this.store.sortInfo) {
9300                     
9301                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9302                     e.select('i', true).addClass(['fa-arrow-up']);
9303                 }
9304                 
9305                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9306                     e.select('i', true).addClass(['fa-arrow-down']);
9307                 }
9308             }
9309         });
9310         
9311         var tbody =  this.bodyEl;
9312               
9313         if(ds.getCount() > 0){
9314             ds.data.each(function(d,rowIndex){
9315                 var row =  this.renderRow(cm, ds, rowIndex);
9316                 
9317                 tbody.createChild(row);
9318                 
9319                 var _this = this;
9320                 
9321                 if(row.cellObjects.length){
9322                     Roo.each(row.cellObjects, function(r){
9323                         _this.renderCellObject(r);
9324                     })
9325                 }
9326                 
9327             }, this);
9328         }
9329         
9330         var tfoot = this.el.select('tfoot', true).first();
9331         
9332         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9333             
9334             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9335             
9336             var total = this.ds.getTotalCount();
9337             
9338             if(this.footer.pageSize < total){
9339                 this.mainFoot.show();
9340             }
9341         }
9342         
9343         Roo.each(this.el.select('tbody td', true).elements, function(e){
9344             e.on('mouseover', _this.onMouseover, _this);
9345         });
9346         
9347         Roo.each(this.el.select('tbody td', true).elements, function(e){
9348             e.on('mouseout', _this.onMouseout, _this);
9349         });
9350         this.fireEvent('rowsrendered', this);
9351         
9352         this.autoSize();
9353     },
9354     
9355     
9356     onUpdate : function(ds,record)
9357     {
9358         this.refreshRow(record);
9359         this.autoSize();
9360     },
9361     
9362     onRemove : function(ds, record, index, isUpdate){
9363         if(isUpdate !== true){
9364             this.fireEvent("beforerowremoved", this, index, record);
9365         }
9366         var bt = this.bodyEl.dom;
9367         
9368         var rows = this.el.select('tbody > tr', true).elements;
9369         
9370         if(typeof(rows[index]) != 'undefined'){
9371             bt.removeChild(rows[index].dom);
9372         }
9373         
9374 //        if(bt.rows[index]){
9375 //            bt.removeChild(bt.rows[index]);
9376 //        }
9377         
9378         if(isUpdate !== true){
9379             //this.stripeRows(index);
9380             //this.syncRowHeights(index, index);
9381             //this.layout();
9382             this.fireEvent("rowremoved", this, index, record);
9383         }
9384     },
9385     
9386     onAdd : function(ds, records, rowIndex)
9387     {
9388         //Roo.log('on Add called');
9389         // - note this does not handle multiple adding very well..
9390         var bt = this.bodyEl.dom;
9391         for (var i =0 ; i < records.length;i++) {
9392             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9393             //Roo.log(records[i]);
9394             //Roo.log(this.store.getAt(rowIndex+i));
9395             this.insertRow(this.store, rowIndex + i, false);
9396             return;
9397         }
9398         
9399     },
9400     
9401     
9402     refreshRow : function(record){
9403         var ds = this.store, index;
9404         if(typeof record == 'number'){
9405             index = record;
9406             record = ds.getAt(index);
9407         }else{
9408             index = ds.indexOf(record);
9409             if (index < 0) {
9410                 return; // should not happen - but seems to 
9411             }
9412         }
9413         this.insertRow(ds, index, true);
9414         this.autoSize();
9415         this.onRemove(ds, record, index+1, true);
9416         this.autoSize();
9417         //this.syncRowHeights(index, index);
9418         //this.layout();
9419         this.fireEvent("rowupdated", this, index, record);
9420     },
9421     
9422     onRowSelect : function(rowIndex){
9423         var row = this.getRowDom(rowIndex);
9424         row.addClass(['bg-info','info']);
9425     },
9426
9427     onRowDeselect : function(rowIndex){
9428         var row = this.getRowDom(rowIndex);
9429         row.removeClass(['bg-info','info']);
9430     },
9431       /**
9432      * Focuses the specified row.
9433      * @param {Number} row The row index
9434      */
9435     focusRow : function(row)
9436     {
9437         //Roo.log('GridView.focusRow');
9438         var x = this.bodyEl.dom.scrollLeft;
9439         this.focusCell(row, 0, false);
9440         this.bodyEl.dom.scrollLeft = x;
9441
9442     },
9443      /**
9444      * Focuses the specified cell.
9445      * @param {Number} row The row index
9446      * @param {Number} col The column index
9447      * @param {Boolean} hscroll false to disable horizontal scrolling
9448      */
9449     focusCell : function(row, col, hscroll)
9450     {
9451         //Roo.log('GridView.focusCell');
9452         var el = this.ensureVisible(row, col, hscroll);
9453         // not sure what focusEL achives = it's a <a> pos relative 
9454         //this.focusEl.alignTo(el, "tl-tl");
9455         //if(Roo.isGecko){
9456         //    this.focusEl.focus();
9457         //}else{
9458         //    this.focusEl.focus.defer(1, this.focusEl);
9459         //}
9460     },
9461     
9462      /**
9463      * Scrolls the specified cell into view
9464      * @param {Number} row The row index
9465      * @param {Number} col The column index
9466      * @param {Boolean} hscroll false to disable horizontal scrolling
9467      */
9468     ensureVisible : function(row, col, hscroll)
9469     {
9470         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9471         //return null; //disable for testing.
9472         if(typeof row != "number"){
9473             row = row.rowIndex;
9474         }
9475         if(row < 0 && row >= this.ds.getCount()){
9476             return  null;
9477         }
9478         col = (col !== undefined ? col : 0);
9479         var cm = this.cm;
9480         while(cm.isHidden(col)){
9481             col++;
9482         }
9483
9484         var el = this.getCellDom(row, col);
9485         if(!el){
9486             return null;
9487         }
9488         var c = this.bodyEl.dom;
9489
9490         var ctop = parseInt(el.offsetTop, 10);
9491         var cleft = parseInt(el.offsetLeft, 10);
9492         var cbot = ctop + el.offsetHeight;
9493         var cright = cleft + el.offsetWidth;
9494
9495         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9496         var ch = 0; //?? header is not withing the area?
9497         var stop = parseInt(c.scrollTop, 10);
9498         var sleft = parseInt(c.scrollLeft, 10);
9499         var sbot = stop + ch;
9500         var sright = sleft + c.clientWidth;
9501         /*
9502         Roo.log('GridView.ensureVisible:' +
9503                 ' ctop:' + ctop +
9504                 ' c.clientHeight:' + c.clientHeight +
9505                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9506                 ' stop:' + stop +
9507                 ' cbot:' + cbot +
9508                 ' sbot:' + sbot +
9509                 ' ch:' + ch  
9510                 );
9511         */
9512         if(ctop < stop){
9513             c.scrollTop = ctop;
9514             //Roo.log("set scrolltop to ctop DISABLE?");
9515         }else if(cbot > sbot){
9516             //Roo.log("set scrolltop to cbot-ch");
9517             c.scrollTop = cbot-ch;
9518         }
9519
9520         if(hscroll !== false){
9521             if(cleft < sleft){
9522                 c.scrollLeft = cleft;
9523             }else if(cright > sright){
9524                 c.scrollLeft = cright-c.clientWidth;
9525             }
9526         }
9527
9528         return el;
9529     },
9530     
9531     
9532     insertRow : function(dm, rowIndex, isUpdate){
9533         
9534         if(!isUpdate){
9535             this.fireEvent("beforerowsinserted", this, rowIndex);
9536         }
9537             //var s = this.getScrollState();
9538         var row = this.renderRow(this.cm, this.store, rowIndex);
9539         // insert before rowIndex..
9540         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9541         
9542         var _this = this;
9543                 
9544         if(row.cellObjects.length){
9545             Roo.each(row.cellObjects, function(r){
9546                 _this.renderCellObject(r);
9547             })
9548         }
9549             
9550         if(!isUpdate){
9551             this.fireEvent("rowsinserted", this, rowIndex);
9552             //this.syncRowHeights(firstRow, lastRow);
9553             //this.stripeRows(firstRow);
9554             //this.layout();
9555         }
9556         
9557     },
9558     
9559     
9560     getRowDom : function(rowIndex)
9561     {
9562         var rows = this.el.select('tbody > tr', true).elements;
9563         
9564         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9565         
9566     },
9567     getCellDom : function(rowIndex, colIndex)
9568     {
9569         var row = this.getRowDom(rowIndex);
9570         if (row === false) {
9571             return false;
9572         }
9573         var cols = row.select('td', true).elements;
9574         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9575         
9576     },
9577     
9578     // returns the object tree for a tr..
9579   
9580     
9581     renderRow : function(cm, ds, rowIndex) 
9582     {
9583         var d = ds.getAt(rowIndex);
9584         
9585         var row = {
9586             tag : 'tr',
9587             cls : 'x-row-' + rowIndex,
9588             cn : []
9589         };
9590             
9591         var cellObjects = [];
9592         
9593         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9594             var config = cm.config[i];
9595             
9596             var renderer = cm.getRenderer(i);
9597             var value = '';
9598             var id = false;
9599             
9600             if(typeof(renderer) !== 'undefined'){
9601                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9602             }
9603             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9604             // and are rendered into the cells after the row is rendered - using the id for the element.
9605             
9606             if(typeof(value) === 'object'){
9607                 id = Roo.id();
9608                 cellObjects.push({
9609                     container : id,
9610                     cfg : value 
9611                 })
9612             }
9613             
9614             var rowcfg = {
9615                 record: d,
9616                 rowIndex : rowIndex,
9617                 colIndex : i,
9618                 rowClass : ''
9619             };
9620
9621             this.fireEvent('rowclass', this, rowcfg);
9622             
9623             var td = {
9624                 tag: 'td',
9625                 // this might end up displaying HTML?
9626                 // this is too messy... - better to only do it on columsn you know are going to be too long
9627                 //tooltip : (typeof(value) === 'object') ? '' : value,
9628                 cls : rowcfg.rowClass + ' x-col-' + i,
9629                 style: '',
9630                 html: (typeof(value) === 'object') ? '' : value
9631             };
9632             
9633             if (id) {
9634                 td.id = id;
9635             }
9636             
9637             if(typeof(config.colspan) != 'undefined'){
9638                 td.colspan = config.colspan;
9639             }
9640             
9641             if(typeof(config.hidden) != 'undefined' && config.hidden){
9642                 td.cls += ' d-none';
9643             } else {
9644                 td.cls += ' d-block';
9645             }
9646             
9647             if(typeof(config.align) != 'undefined' && config.align.length){
9648                 td.style += ' text-align:' + config.align + ';';
9649             }
9650             if(typeof(config.valign) != 'undefined' && config.valign.length){
9651                 td.style += ' vertical-align:' + config.valign + ';';
9652             }
9653             
9654             if(typeof(config.width) != 'undefined'){
9655                 td.style += ' width:' +  config.width + 'px;';
9656             }
9657             
9658             if(typeof(config.cursor) != 'undefined'){
9659                 td.style += ' cursor:' +  config.cursor + ';';
9660             }
9661             
9662             if(typeof(config.cls) != 'undefined'){
9663                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9664             }
9665             
9666             ['xs','sm','md','lg'].map(function(size){
9667                 
9668                 if(typeof(config[size]) == 'undefined'){
9669                     return;
9670                 }
9671                 
9672                 
9673                   
9674                 if (!config[size]) { // 0 = hidden
9675                     // BS 4 '0' is treated as hide that column and below.
9676                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9677                     return;
9678                 }
9679                 
9680                 td.cls += ' col-' + size + '-' + config[size] + (
9681                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9682                 );
9683                  
9684
9685             });
9686             
9687             row.cn.push(td);
9688            
9689         }
9690         
9691         row.cellObjects = cellObjects;
9692         
9693         return row;
9694           
9695     },
9696     
9697     
9698     
9699     onBeforeLoad : function()
9700     {
9701         
9702     },
9703      /**
9704      * Remove all rows
9705      */
9706     clear : function()
9707     {
9708         this.el.select('tbody', true).first().dom.innerHTML = '';
9709     },
9710     /**
9711      * Show or hide a row.
9712      * @param {Number} rowIndex to show or hide
9713      * @param {Boolean} state hide
9714      */
9715     setRowVisibility : function(rowIndex, state)
9716     {
9717         var bt = this.bodyEl.dom;
9718         
9719         var rows = this.el.select('tbody > tr', true).elements;
9720         
9721         if(typeof(rows[rowIndex]) == 'undefined'){
9722             return;
9723         }
9724         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9725         
9726     },
9727     
9728     
9729     getSelectionModel : function(){
9730         if(!this.selModel){
9731             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9732         }
9733         return this.selModel;
9734     },
9735     /*
9736      * Render the Roo.bootstrap object from renderder
9737      */
9738     renderCellObject : function(r)
9739     {
9740         var _this = this;
9741         
9742         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9743         
9744         var t = r.cfg.render(r.container);
9745         
9746         if(r.cfg.cn){
9747             Roo.each(r.cfg.cn, function(c){
9748                 var child = {
9749                     container: t.getChildContainer(),
9750                     cfg: c
9751                 };
9752                 _this.renderCellObject(child);
9753             })
9754         }
9755     },
9756     
9757     getRowIndex : function(row)
9758     {
9759         var rowIndex = -1;
9760         
9761         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9762             if(el != row){
9763                 return;
9764             }
9765             
9766             rowIndex = index;
9767         });
9768         
9769         return rowIndex;
9770     },
9771      /**
9772      * Returns the grid's underlying element = used by panel.Grid
9773      * @return {Element} The element
9774      */
9775     getGridEl : function(){
9776         return this.el;
9777     },
9778      /**
9779      * Forces a resize - used by panel.Grid
9780      * @return {Element} The element
9781      */
9782     autoSize : function()
9783     {
9784         //var ctr = Roo.get(this.container.dom.parentElement);
9785         var ctr = Roo.get(this.el.dom);
9786         
9787         var thd = this.getGridEl().select('thead',true).first();
9788         var tbd = this.getGridEl().select('tbody', true).first();
9789         var tfd = this.getGridEl().select('tfoot', true).first();
9790         
9791         var cw = ctr.getWidth();
9792         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9793         
9794         if (tbd) {
9795             
9796             tbd.setWidth(ctr.getWidth());
9797             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9798             // this needs fixing for various usage - currently only hydra job advers I think..
9799             //tdb.setHeight(
9800             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9801             //); 
9802             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9803             cw -= barsize;
9804         }
9805         cw = Math.max(cw, this.totalWidth);
9806         this.getGridEl().select('tbody tr',true).setWidth(cw);
9807         
9808         // resize 'expandable coloumn?
9809         
9810         return; // we doe not have a view in this design..
9811         
9812     },
9813     onBodyScroll: function()
9814     {
9815         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
9816         if(this.headEl){
9817             this.headEl.setStyle({
9818                 'position' : 'relative',
9819                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
9820             });
9821         }
9822         
9823         if(this.lazyLoad){
9824             
9825             var scrollHeight = this.bodyEl.dom.scrollHeight;
9826             
9827             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
9828             
9829             var height = this.bodyEl.getHeight();
9830             
9831             if(scrollHeight - height == scrollTop) {
9832                 
9833                 var total = this.ds.getTotalCount();
9834                 
9835                 if(this.footer.cursor + this.footer.pageSize < total){
9836                     
9837                     this.footer.ds.load({
9838                         params : {
9839                             start : this.footer.cursor + this.footer.pageSize,
9840                             limit : this.footer.pageSize
9841                         },
9842                         add : true
9843                     });
9844                 }
9845             }
9846             
9847         }
9848     },
9849     
9850     onHeaderChange : function()
9851     {
9852         var header = this.renderHeader();
9853         var table = this.el.select('table', true).first();
9854         
9855         this.headEl.remove();
9856         this.headEl = table.createChild(header, this.bodyEl, false);
9857         
9858         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9859             e.on('click', this.sort, this);
9860         }, this);
9861         
9862         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9863             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9864         }
9865         
9866     },
9867     
9868     onHiddenChange : function(colModel, colIndex, hidden)
9869     {
9870         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9871         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9872         
9873         //this.CSS.updateRule(thSelector, "display", "");
9874         var cols = this.headEl.select('th', true).elements;
9875         if (typeof(cols[colIndex]) != 'undefined') {
9876             cols[colIndex].removeClass(['d-none', 'd-block']);
9877             cols[colIndex].addClass( hidden ? 'd-none' : 'd-block');
9878         }
9879         this.CSS.updateRule(tdSelector, "display", "");
9880         
9881         if(hidden){
9882           //  this.CSS.updateRule(thSelector, "display", "none");
9883             this.CSS.updateRule(tdSelector, "display", "none");
9884         }
9885         
9886         this.onHeaderChange();
9887         this.onLoad();
9888     },
9889     
9890     setColumnWidth: function(col_index, width)
9891     {
9892         // width = "md-2 xs-2..."
9893         if(!this.colModel.config[col_index]) {
9894             return;
9895         }
9896         
9897         var w = width.split(" ");
9898         
9899         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9900         
9901         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9902         
9903         
9904         for(var j = 0; j < w.length; j++) {
9905             
9906             if(!w[j]) {
9907                 continue;
9908             }
9909             
9910             var size_cls = w[j].split("-");
9911             
9912             if(!Number.isInteger(size_cls[1] * 1)) {
9913                 continue;
9914             }
9915             
9916             if(!this.colModel.config[col_index][size_cls[0]]) {
9917                 continue;
9918             }
9919             
9920             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9921                 continue;
9922             }
9923             
9924             h_row[0].classList.replace(
9925                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9926                 "col-"+size_cls[0]+"-"+size_cls[1]
9927             );
9928             
9929             for(var i = 0; i < rows.length; i++) {
9930                 
9931                 var size_cls = w[j].split("-");
9932                 
9933                 if(!Number.isInteger(size_cls[1] * 1)) {
9934                     continue;
9935                 }
9936                 
9937                 if(!this.colModel.config[col_index][size_cls[0]]) {
9938                     continue;
9939                 }
9940                 
9941                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9942                     continue;
9943                 }
9944                 
9945                 rows[i].classList.replace(
9946                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9947                     "col-"+size_cls[0]+"-"+size_cls[1]
9948                 );
9949             }
9950             
9951             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9952         }
9953     }
9954 });
9955
9956  
9957
9958 /**
9959  * @depricated
9960 */
9961 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
9962 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;/*
9963  * - LGPL
9964  *
9965  * table cell
9966  * 
9967  */
9968
9969 /**
9970  * @class Roo.bootstrap.TableCell
9971  * @extends Roo.bootstrap.Component
9972  * Bootstrap TableCell class
9973  * @cfg {String} html cell contain text
9974  * @cfg {String} cls cell class
9975  * @cfg {String} tag cell tag (td|th) default td
9976  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9977  * @cfg {String} align Aligns the content in a cell
9978  * @cfg {String} axis Categorizes cells
9979  * @cfg {String} bgcolor Specifies the background color of a cell
9980  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9981  * @cfg {Number} colspan Specifies the number of columns a cell should span
9982  * @cfg {String} headers Specifies one or more header cells a cell is related to
9983  * @cfg {Number} height Sets the height of a cell
9984  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9985  * @cfg {Number} rowspan Sets the number of rows a cell should span
9986  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9987  * @cfg {String} valign Vertical aligns the content in a cell
9988  * @cfg {Number} width Specifies the width of a cell
9989  * 
9990  * @constructor
9991  * Create a new TableCell
9992  * @param {Object} config The config object
9993  */
9994
9995 Roo.bootstrap.TableCell = function(config){
9996     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9997 };
9998
9999 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10000     
10001     html: false,
10002     cls: false,
10003     tag: false,
10004     abbr: false,
10005     align: false,
10006     axis: false,
10007     bgcolor: false,
10008     charoff: false,
10009     colspan: false,
10010     headers: false,
10011     height: false,
10012     nowrap: false,
10013     rowspan: false,
10014     scope: false,
10015     valign: false,
10016     width: false,
10017     
10018     
10019     getAutoCreate : function(){
10020         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10021         
10022         cfg = {
10023             tag: 'td'
10024         };
10025         
10026         if(this.tag){
10027             cfg.tag = this.tag;
10028         }
10029         
10030         if (this.html) {
10031             cfg.html=this.html
10032         }
10033         if (this.cls) {
10034             cfg.cls=this.cls
10035         }
10036         if (this.abbr) {
10037             cfg.abbr=this.abbr
10038         }
10039         if (this.align) {
10040             cfg.align=this.align
10041         }
10042         if (this.axis) {
10043             cfg.axis=this.axis
10044         }
10045         if (this.bgcolor) {
10046             cfg.bgcolor=this.bgcolor
10047         }
10048         if (this.charoff) {
10049             cfg.charoff=this.charoff
10050         }
10051         if (this.colspan) {
10052             cfg.colspan=this.colspan
10053         }
10054         if (this.headers) {
10055             cfg.headers=this.headers
10056         }
10057         if (this.height) {
10058             cfg.height=this.height
10059         }
10060         if (this.nowrap) {
10061             cfg.nowrap=this.nowrap
10062         }
10063         if (this.rowspan) {
10064             cfg.rowspan=this.rowspan
10065         }
10066         if (this.scope) {
10067             cfg.scope=this.scope
10068         }
10069         if (this.valign) {
10070             cfg.valign=this.valign
10071         }
10072         if (this.width) {
10073             cfg.width=this.width
10074         }
10075         
10076         
10077         return cfg;
10078     }
10079    
10080 });
10081
10082  
10083
10084  /*
10085  * - LGPL
10086  *
10087  * table row
10088  * 
10089  */
10090
10091 /**
10092  * @class Roo.bootstrap.TableRow
10093  * @extends Roo.bootstrap.Component
10094  * Bootstrap TableRow class
10095  * @cfg {String} cls row class
10096  * @cfg {String} align Aligns the content in a table row
10097  * @cfg {String} bgcolor Specifies a background color for a table row
10098  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10099  * @cfg {String} valign Vertical aligns the content in a table row
10100  * 
10101  * @constructor
10102  * Create a new TableRow
10103  * @param {Object} config The config object
10104  */
10105
10106 Roo.bootstrap.TableRow = function(config){
10107     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10108 };
10109
10110 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10111     
10112     cls: false,
10113     align: false,
10114     bgcolor: false,
10115     charoff: false,
10116     valign: false,
10117     
10118     getAutoCreate : function(){
10119         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10120         
10121         cfg = {
10122             tag: 'tr'
10123         };
10124             
10125         if(this.cls){
10126             cfg.cls = this.cls;
10127         }
10128         if(this.align){
10129             cfg.align = this.align;
10130         }
10131         if(this.bgcolor){
10132             cfg.bgcolor = this.bgcolor;
10133         }
10134         if(this.charoff){
10135             cfg.charoff = this.charoff;
10136         }
10137         if(this.valign){
10138             cfg.valign = this.valign;
10139         }
10140         
10141         return cfg;
10142     }
10143    
10144 });
10145
10146  
10147
10148  /*
10149  * - LGPL
10150  *
10151  * table body
10152  * 
10153  */
10154
10155 /**
10156  * @class Roo.bootstrap.TableBody
10157  * @extends Roo.bootstrap.Component
10158  * Bootstrap TableBody class
10159  * @cfg {String} cls element class
10160  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10161  * @cfg {String} align Aligns the content inside the element
10162  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10163  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10164  * 
10165  * @constructor
10166  * Create a new TableBody
10167  * @param {Object} config The config object
10168  */
10169
10170 Roo.bootstrap.TableBody = function(config){
10171     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10172 };
10173
10174 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10175     
10176     cls: false,
10177     tag: false,
10178     align: false,
10179     charoff: false,
10180     valign: false,
10181     
10182     getAutoCreate : function(){
10183         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10184         
10185         cfg = {
10186             tag: 'tbody'
10187         };
10188             
10189         if (this.cls) {
10190             cfg.cls=this.cls
10191         }
10192         if(this.tag){
10193             cfg.tag = this.tag;
10194         }
10195         
10196         if(this.align){
10197             cfg.align = this.align;
10198         }
10199         if(this.charoff){
10200             cfg.charoff = this.charoff;
10201         }
10202         if(this.valign){
10203             cfg.valign = this.valign;
10204         }
10205         
10206         return cfg;
10207     }
10208     
10209     
10210 //    initEvents : function()
10211 //    {
10212 //        
10213 //        if(!this.store){
10214 //            return;
10215 //        }
10216 //        
10217 //        this.store = Roo.factory(this.store, Roo.data);
10218 //        this.store.on('load', this.onLoad, this);
10219 //        
10220 //        this.store.load();
10221 //        
10222 //    },
10223 //    
10224 //    onLoad: function () 
10225 //    {   
10226 //        this.fireEvent('load', this);
10227 //    }
10228 //    
10229 //   
10230 });
10231
10232  
10233
10234  /*
10235  * Based on:
10236  * Ext JS Library 1.1.1
10237  * Copyright(c) 2006-2007, Ext JS, LLC.
10238  *
10239  * Originally Released Under LGPL - original licence link has changed is not relivant.
10240  *
10241  * Fork - LGPL
10242  * <script type="text/javascript">
10243  */
10244
10245 // as we use this in bootstrap.
10246 Roo.namespace('Roo.form');
10247  /**
10248  * @class Roo.form.Action
10249  * Internal Class used to handle form actions
10250  * @constructor
10251  * @param {Roo.form.BasicForm} el The form element or its id
10252  * @param {Object} config Configuration options
10253  */
10254
10255  
10256  
10257 // define the action interface
10258 Roo.form.Action = function(form, options){
10259     this.form = form;
10260     this.options = options || {};
10261 };
10262 /**
10263  * Client Validation Failed
10264  * @const 
10265  */
10266 Roo.form.Action.CLIENT_INVALID = 'client';
10267 /**
10268  * Server Validation Failed
10269  * @const 
10270  */
10271 Roo.form.Action.SERVER_INVALID = 'server';
10272  /**
10273  * Connect to Server Failed
10274  * @const 
10275  */
10276 Roo.form.Action.CONNECT_FAILURE = 'connect';
10277 /**
10278  * Reading Data from Server Failed
10279  * @const 
10280  */
10281 Roo.form.Action.LOAD_FAILURE = 'load';
10282
10283 Roo.form.Action.prototype = {
10284     type : 'default',
10285     failureType : undefined,
10286     response : undefined,
10287     result : undefined,
10288
10289     // interface method
10290     run : function(options){
10291
10292     },
10293
10294     // interface method
10295     success : function(response){
10296
10297     },
10298
10299     // interface method
10300     handleResponse : function(response){
10301
10302     },
10303
10304     // default connection failure
10305     failure : function(response){
10306         
10307         this.response = response;
10308         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10309         this.form.afterAction(this, false);
10310     },
10311
10312     processResponse : function(response){
10313         this.response = response;
10314         if(!response.responseText){
10315             return true;
10316         }
10317         this.result = this.handleResponse(response);
10318         return this.result;
10319     },
10320
10321     // utility functions used internally
10322     getUrl : function(appendParams){
10323         var url = this.options.url || this.form.url || this.form.el.dom.action;
10324         if(appendParams){
10325             var p = this.getParams();
10326             if(p){
10327                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10328             }
10329         }
10330         return url;
10331     },
10332
10333     getMethod : function(){
10334         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10335     },
10336
10337     getParams : function(){
10338         var bp = this.form.baseParams;
10339         var p = this.options.params;
10340         if(p){
10341             if(typeof p == "object"){
10342                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10343             }else if(typeof p == 'string' && bp){
10344                 p += '&' + Roo.urlEncode(bp);
10345             }
10346         }else if(bp){
10347             p = Roo.urlEncode(bp);
10348         }
10349         return p;
10350     },
10351
10352     createCallback : function(){
10353         return {
10354             success: this.success,
10355             failure: this.failure,
10356             scope: this,
10357             timeout: (this.form.timeout*1000),
10358             upload: this.form.fileUpload ? this.success : undefined
10359         };
10360     }
10361 };
10362
10363 Roo.form.Action.Submit = function(form, options){
10364     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10365 };
10366
10367 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10368     type : 'submit',
10369
10370     haveProgress : false,
10371     uploadComplete : false,
10372     
10373     // uploadProgress indicator.
10374     uploadProgress : function()
10375     {
10376         if (!this.form.progressUrl) {
10377             return;
10378         }
10379         
10380         if (!this.haveProgress) {
10381             Roo.MessageBox.progress("Uploading", "Uploading");
10382         }
10383         if (this.uploadComplete) {
10384            Roo.MessageBox.hide();
10385            return;
10386         }
10387         
10388         this.haveProgress = true;
10389    
10390         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10391         
10392         var c = new Roo.data.Connection();
10393         c.request({
10394             url : this.form.progressUrl,
10395             params: {
10396                 id : uid
10397             },
10398             method: 'GET',
10399             success : function(req){
10400                //console.log(data);
10401                 var rdata = false;
10402                 var edata;
10403                 try  {
10404                    rdata = Roo.decode(req.responseText)
10405                 } catch (e) {
10406                     Roo.log("Invalid data from server..");
10407                     Roo.log(edata);
10408                     return;
10409                 }
10410                 if (!rdata || !rdata.success) {
10411                     Roo.log(rdata);
10412                     Roo.MessageBox.alert(Roo.encode(rdata));
10413                     return;
10414                 }
10415                 var data = rdata.data;
10416                 
10417                 if (this.uploadComplete) {
10418                    Roo.MessageBox.hide();
10419                    return;
10420                 }
10421                    
10422                 if (data){
10423                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10424                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10425                     );
10426                 }
10427                 this.uploadProgress.defer(2000,this);
10428             },
10429        
10430             failure: function(data) {
10431                 Roo.log('progress url failed ');
10432                 Roo.log(data);
10433             },
10434             scope : this
10435         });
10436            
10437     },
10438     
10439     
10440     run : function()
10441     {
10442         // run get Values on the form, so it syncs any secondary forms.
10443         this.form.getValues();
10444         
10445         var o = this.options;
10446         var method = this.getMethod();
10447         var isPost = method == 'POST';
10448         if(o.clientValidation === false || this.form.isValid()){
10449             
10450             if (this.form.progressUrl) {
10451                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10452                     (new Date() * 1) + '' + Math.random());
10453                     
10454             } 
10455             
10456             
10457             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10458                 form:this.form.el.dom,
10459                 url:this.getUrl(!isPost),
10460                 method: method,
10461                 params:isPost ? this.getParams() : null,
10462                 isUpload: this.form.fileUpload,
10463                 formData : this.form.formData
10464             }));
10465             
10466             this.uploadProgress();
10467
10468         }else if (o.clientValidation !== false){ // client validation failed
10469             this.failureType = Roo.form.Action.CLIENT_INVALID;
10470             this.form.afterAction(this, false);
10471         }
10472     },
10473
10474     success : function(response)
10475     {
10476         this.uploadComplete= true;
10477         if (this.haveProgress) {
10478             Roo.MessageBox.hide();
10479         }
10480         
10481         
10482         var result = this.processResponse(response);
10483         if(result === true || result.success){
10484             this.form.afterAction(this, true);
10485             return;
10486         }
10487         if(result.errors){
10488             this.form.markInvalid(result.errors);
10489             this.failureType = Roo.form.Action.SERVER_INVALID;
10490         }
10491         this.form.afterAction(this, false);
10492     },
10493     failure : function(response)
10494     {
10495         this.uploadComplete= true;
10496         if (this.haveProgress) {
10497             Roo.MessageBox.hide();
10498         }
10499         
10500         this.response = response;
10501         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10502         this.form.afterAction(this, false);
10503     },
10504     
10505     handleResponse : function(response){
10506         if(this.form.errorReader){
10507             var rs = this.form.errorReader.read(response);
10508             var errors = [];
10509             if(rs.records){
10510                 for(var i = 0, len = rs.records.length; i < len; i++) {
10511                     var r = rs.records[i];
10512                     errors[i] = r.data;
10513                 }
10514             }
10515             if(errors.length < 1){
10516                 errors = null;
10517             }
10518             return {
10519                 success : rs.success,
10520                 errors : errors
10521             };
10522         }
10523         var ret = false;
10524         try {
10525             ret = Roo.decode(response.responseText);
10526         } catch (e) {
10527             ret = {
10528                 success: false,
10529                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10530                 errors : []
10531             };
10532         }
10533         return ret;
10534         
10535     }
10536 });
10537
10538
10539 Roo.form.Action.Load = function(form, options){
10540     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10541     this.reader = this.form.reader;
10542 };
10543
10544 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10545     type : 'load',
10546
10547     run : function(){
10548         
10549         Roo.Ajax.request(Roo.apply(
10550                 this.createCallback(), {
10551                     method:this.getMethod(),
10552                     url:this.getUrl(false),
10553                     params:this.getParams()
10554         }));
10555     },
10556
10557     success : function(response){
10558         
10559         var result = this.processResponse(response);
10560         if(result === true || !result.success || !result.data){
10561             this.failureType = Roo.form.Action.LOAD_FAILURE;
10562             this.form.afterAction(this, false);
10563             return;
10564         }
10565         this.form.clearInvalid();
10566         this.form.setValues(result.data);
10567         this.form.afterAction(this, true);
10568     },
10569
10570     handleResponse : function(response){
10571         if(this.form.reader){
10572             var rs = this.form.reader.read(response);
10573             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10574             return {
10575                 success : rs.success,
10576                 data : data
10577             };
10578         }
10579         return Roo.decode(response.responseText);
10580     }
10581 });
10582
10583 Roo.form.Action.ACTION_TYPES = {
10584     'load' : Roo.form.Action.Load,
10585     'submit' : Roo.form.Action.Submit
10586 };/*
10587  * - LGPL
10588  *
10589  * form
10590  *
10591  */
10592
10593 /**
10594  * @class Roo.bootstrap.Form
10595  * @extends Roo.bootstrap.Component
10596  * Bootstrap Form class
10597  * @cfg {String} method  GET | POST (default POST)
10598  * @cfg {String} labelAlign top | left (default top)
10599  * @cfg {String} align left  | right - for navbars
10600  * @cfg {Boolean} loadMask load mask when submit (default true)
10601
10602  *
10603  * @constructor
10604  * Create a new Form
10605  * @param {Object} config The config object
10606  */
10607
10608
10609 Roo.bootstrap.Form = function(config){
10610     
10611     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10612     
10613     Roo.bootstrap.Form.popover.apply();
10614     
10615     this.addEvents({
10616         /**
10617          * @event clientvalidation
10618          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10619          * @param {Form} this
10620          * @param {Boolean} valid true if the form has passed client-side validation
10621          */
10622         clientvalidation: true,
10623         /**
10624          * @event beforeaction
10625          * Fires before any action is performed. Return false to cancel the action.
10626          * @param {Form} this
10627          * @param {Action} action The action to be performed
10628          */
10629         beforeaction: true,
10630         /**
10631          * @event actionfailed
10632          * Fires when an action fails.
10633          * @param {Form} this
10634          * @param {Action} action The action that failed
10635          */
10636         actionfailed : true,
10637         /**
10638          * @event actioncomplete
10639          * Fires when an action is completed.
10640          * @param {Form} this
10641          * @param {Action} action The action that completed
10642          */
10643         actioncomplete : true
10644     });
10645 };
10646
10647 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10648
10649      /**
10650      * @cfg {String} method
10651      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10652      */
10653     method : 'POST',
10654     /**
10655      * @cfg {String} url
10656      * The URL to use for form actions if one isn't supplied in the action options.
10657      */
10658     /**
10659      * @cfg {Boolean} fileUpload
10660      * Set to true if this form is a file upload.
10661      */
10662
10663     /**
10664      * @cfg {Object} baseParams
10665      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10666      */
10667
10668     /**
10669      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10670      */
10671     timeout: 30,
10672     /**
10673      * @cfg {Sting} align (left|right) for navbar forms
10674      */
10675     align : 'left',
10676
10677     // private
10678     activeAction : null,
10679
10680     /**
10681      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10682      * element by passing it or its id or mask the form itself by passing in true.
10683      * @type Mixed
10684      */
10685     waitMsgTarget : false,
10686
10687     loadMask : true,
10688     
10689     /**
10690      * @cfg {Boolean} errorMask (true|false) default false
10691      */
10692     errorMask : false,
10693     
10694     /**
10695      * @cfg {Number} maskOffset Default 100
10696      */
10697     maskOffset : 100,
10698     
10699     /**
10700      * @cfg {Boolean} maskBody
10701      */
10702     maskBody : false,
10703
10704     getAutoCreate : function(){
10705
10706         var cfg = {
10707             tag: 'form',
10708             method : this.method || 'POST',
10709             id : this.id || Roo.id(),
10710             cls : ''
10711         };
10712         if (this.parent().xtype.match(/^Nav/)) {
10713             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10714
10715         }
10716
10717         if (this.labelAlign == 'left' ) {
10718             cfg.cls += ' form-horizontal';
10719         }
10720
10721
10722         return cfg;
10723     },
10724     initEvents : function()
10725     {
10726         this.el.on('submit', this.onSubmit, this);
10727         // this was added as random key presses on the form where triggering form submit.
10728         this.el.on('keypress', function(e) {
10729             if (e.getCharCode() != 13) {
10730                 return true;
10731             }
10732             // we might need to allow it for textareas.. and some other items.
10733             // check e.getTarget().
10734
10735             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10736                 return true;
10737             }
10738
10739             Roo.log("keypress blocked");
10740
10741             e.preventDefault();
10742             return false;
10743         });
10744         
10745     },
10746     // private
10747     onSubmit : function(e){
10748         e.stopEvent();
10749     },
10750
10751      /**
10752      * Returns true if client-side validation on the form is successful.
10753      * @return Boolean
10754      */
10755     isValid : function(){
10756         var items = this.getItems();
10757         var valid = true;
10758         var target = false;
10759         
10760         items.each(function(f){
10761             
10762             if(f.validate()){
10763                 return;
10764             }
10765             
10766             Roo.log('invalid field: ' + f.name);
10767             
10768             valid = false;
10769
10770             if(!target && f.el.isVisible(true)){
10771                 target = f;
10772             }
10773            
10774         });
10775         
10776         if(this.errorMask && !valid){
10777             Roo.bootstrap.Form.popover.mask(this, target);
10778         }
10779         
10780         return valid;
10781     },
10782     
10783     /**
10784      * Returns true if any fields in this form have changed since their original load.
10785      * @return Boolean
10786      */
10787     isDirty : function(){
10788         var dirty = false;
10789         var items = this.getItems();
10790         items.each(function(f){
10791            if(f.isDirty()){
10792                dirty = true;
10793                return false;
10794            }
10795            return true;
10796         });
10797         return dirty;
10798     },
10799      /**
10800      * Performs a predefined action (submit or load) or custom actions you define on this form.
10801      * @param {String} actionName The name of the action type
10802      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10803      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10804      * accept other config options):
10805      * <pre>
10806 Property          Type             Description
10807 ----------------  ---------------  ----------------------------------------------------------------------------------
10808 url               String           The url for the action (defaults to the form's url)
10809 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10810 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10811 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10812                                    validate the form on the client (defaults to false)
10813      * </pre>
10814      * @return {BasicForm} this
10815      */
10816     doAction : function(action, options){
10817         if(typeof action == 'string'){
10818             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10819         }
10820         if(this.fireEvent('beforeaction', this, action) !== false){
10821             this.beforeAction(action);
10822             action.run.defer(100, action);
10823         }
10824         return this;
10825     },
10826
10827     // private
10828     beforeAction : function(action){
10829         var o = action.options;
10830         
10831         if(this.loadMask){
10832             
10833             if(this.maskBody){
10834                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10835             } else {
10836                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10837             }
10838         }
10839         // not really supported yet.. ??
10840
10841         //if(this.waitMsgTarget === true){
10842         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10843         //}else if(this.waitMsgTarget){
10844         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10845         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10846         //}else {
10847         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10848        // }
10849
10850     },
10851
10852     // private
10853     afterAction : function(action, success){
10854         this.activeAction = null;
10855         var o = action.options;
10856
10857         if(this.loadMask){
10858             
10859             if(this.maskBody){
10860                 Roo.get(document.body).unmask();
10861             } else {
10862                 this.el.unmask();
10863             }
10864         }
10865         
10866         //if(this.waitMsgTarget === true){
10867 //            this.el.unmask();
10868         //}else if(this.waitMsgTarget){
10869         //    this.waitMsgTarget.unmask();
10870         //}else{
10871         //    Roo.MessageBox.updateProgress(1);
10872         //    Roo.MessageBox.hide();
10873        // }
10874         //
10875         if(success){
10876             if(o.reset){
10877                 this.reset();
10878             }
10879             Roo.callback(o.success, o.scope, [this, action]);
10880             this.fireEvent('actioncomplete', this, action);
10881
10882         }else{
10883
10884             // failure condition..
10885             // we have a scenario where updates need confirming.
10886             // eg. if a locking scenario exists..
10887             // we look for { errors : { needs_confirm : true }} in the response.
10888             if (
10889                 (typeof(action.result) != 'undefined')  &&
10890                 (typeof(action.result.errors) != 'undefined')  &&
10891                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10892            ){
10893                 var _t = this;
10894                 Roo.log("not supported yet");
10895                  /*
10896
10897                 Roo.MessageBox.confirm(
10898                     "Change requires confirmation",
10899                     action.result.errorMsg,
10900                     function(r) {
10901                         if (r != 'yes') {
10902                             return;
10903                         }
10904                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10905                     }
10906
10907                 );
10908                 */
10909
10910
10911                 return;
10912             }
10913
10914             Roo.callback(o.failure, o.scope, [this, action]);
10915             // show an error message if no failed handler is set..
10916             if (!this.hasListener('actionfailed')) {
10917                 Roo.log("need to add dialog support");
10918                 /*
10919                 Roo.MessageBox.alert("Error",
10920                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10921                         action.result.errorMsg :
10922                         "Saving Failed, please check your entries or try again"
10923                 );
10924                 */
10925             }
10926
10927             this.fireEvent('actionfailed', this, action);
10928         }
10929
10930     },
10931     /**
10932      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10933      * @param {String} id The value to search for
10934      * @return Field
10935      */
10936     findField : function(id){
10937         var items = this.getItems();
10938         var field = items.get(id);
10939         if(!field){
10940              items.each(function(f){
10941                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10942                     field = f;
10943                     return false;
10944                 }
10945                 return true;
10946             });
10947         }
10948         return field || null;
10949     },
10950      /**
10951      * Mark fields in this form invalid in bulk.
10952      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10953      * @return {BasicForm} this
10954      */
10955     markInvalid : function(errors){
10956         if(errors instanceof Array){
10957             for(var i = 0, len = errors.length; i < len; i++){
10958                 var fieldError = errors[i];
10959                 var f = this.findField(fieldError.id);
10960                 if(f){
10961                     f.markInvalid(fieldError.msg);
10962                 }
10963             }
10964         }else{
10965             var field, id;
10966             for(id in errors){
10967                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10968                     field.markInvalid(errors[id]);
10969                 }
10970             }
10971         }
10972         //Roo.each(this.childForms || [], function (f) {
10973         //    f.markInvalid(errors);
10974         //});
10975
10976         return this;
10977     },
10978
10979     /**
10980      * Set values for fields in this form in bulk.
10981      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10982      * @return {BasicForm} this
10983      */
10984     setValues : function(values){
10985         if(values instanceof Array){ // array of objects
10986             for(var i = 0, len = values.length; i < len; i++){
10987                 var v = values[i];
10988                 var f = this.findField(v.id);
10989                 if(f){
10990                     f.setValue(v.value);
10991                     if(this.trackResetOnLoad){
10992                         f.originalValue = f.getValue();
10993                     }
10994                 }
10995             }
10996         }else{ // object hash
10997             var field, id;
10998             for(id in values){
10999                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11000
11001                     if (field.setFromData &&
11002                         field.valueField &&
11003                         field.displayField &&
11004                         // combos' with local stores can
11005                         // be queried via setValue()
11006                         // to set their value..
11007                         (field.store && !field.store.isLocal)
11008                         ) {
11009                         // it's a combo
11010                         var sd = { };
11011                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11012                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11013                         field.setFromData(sd);
11014
11015                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11016                         
11017                         field.setFromData(values);
11018                         
11019                     } else {
11020                         field.setValue(values[id]);
11021                     }
11022
11023
11024                     if(this.trackResetOnLoad){
11025                         field.originalValue = field.getValue();
11026                     }
11027                 }
11028             }
11029         }
11030
11031         //Roo.each(this.childForms || [], function (f) {
11032         //    f.setValues(values);
11033         //});
11034
11035         return this;
11036     },
11037
11038     /**
11039      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11040      * they are returned as an array.
11041      * @param {Boolean} asString
11042      * @return {Object}
11043      */
11044     getValues : function(asString){
11045         //if (this.childForms) {
11046             // copy values from the child forms
11047         //    Roo.each(this.childForms, function (f) {
11048         //        this.setValues(f.getValues());
11049         //    }, this);
11050         //}
11051
11052
11053
11054         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11055         if(asString === true){
11056             return fs;
11057         }
11058         return Roo.urlDecode(fs);
11059     },
11060
11061     /**
11062      * Returns the fields in this form as an object with key/value pairs.
11063      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11064      * @return {Object}
11065      */
11066     getFieldValues : function(with_hidden)
11067     {
11068         var items = this.getItems();
11069         var ret = {};
11070         items.each(function(f){
11071             
11072             if (!f.getName()) {
11073                 return;
11074             }
11075             
11076             var v = f.getValue();
11077             
11078             if (f.inputType =='radio') {
11079                 if (typeof(ret[f.getName()]) == 'undefined') {
11080                     ret[f.getName()] = ''; // empty..
11081                 }
11082
11083                 if (!f.el.dom.checked) {
11084                     return;
11085
11086                 }
11087                 v = f.el.dom.value;
11088
11089             }
11090             
11091             if(f.xtype == 'MoneyField'){
11092                 ret[f.currencyName] = f.getCurrency();
11093             }
11094
11095             // not sure if this supported any more..
11096             if ((typeof(v) == 'object') && f.getRawValue) {
11097                 v = f.getRawValue() ; // dates..
11098             }
11099             // combo boxes where name != hiddenName...
11100             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11101                 ret[f.name] = f.getRawValue();
11102             }
11103             ret[f.getName()] = v;
11104         });
11105
11106         return ret;
11107     },
11108
11109     /**
11110      * Clears all invalid messages in this form.
11111      * @return {BasicForm} this
11112      */
11113     clearInvalid : function(){
11114         var items = this.getItems();
11115
11116         items.each(function(f){
11117            f.clearInvalid();
11118         });
11119
11120         return this;
11121     },
11122
11123     /**
11124      * Resets this form.
11125      * @return {BasicForm} this
11126      */
11127     reset : function(){
11128         var items = this.getItems();
11129         items.each(function(f){
11130             f.reset();
11131         });
11132
11133         Roo.each(this.childForms || [], function (f) {
11134             f.reset();
11135         });
11136
11137
11138         return this;
11139     },
11140     
11141     getItems : function()
11142     {
11143         var r=new Roo.util.MixedCollection(false, function(o){
11144             return o.id || (o.id = Roo.id());
11145         });
11146         var iter = function(el) {
11147             if (el.inputEl) {
11148                 r.add(el);
11149             }
11150             if (!el.items) {
11151                 return;
11152             }
11153             Roo.each(el.items,function(e) {
11154                 iter(e);
11155             });
11156         };
11157
11158         iter(this);
11159         return r;
11160     },
11161     
11162     hideFields : function(items)
11163     {
11164         Roo.each(items, function(i){
11165             
11166             var f = this.findField(i);
11167             
11168             if(!f){
11169                 return;
11170             }
11171             
11172             f.hide();
11173             
11174         }, this);
11175     },
11176     
11177     showFields : function(items)
11178     {
11179         Roo.each(items, function(i){
11180             
11181             var f = this.findField(i);
11182             
11183             if(!f){
11184                 return;
11185             }
11186             
11187             f.show();
11188             
11189         }, this);
11190     }
11191
11192 });
11193
11194 Roo.apply(Roo.bootstrap.Form, {
11195     
11196     popover : {
11197         
11198         padding : 5,
11199         
11200         isApplied : false,
11201         
11202         isMasked : false,
11203         
11204         form : false,
11205         
11206         target : false,
11207         
11208         toolTip : false,
11209         
11210         intervalID : false,
11211         
11212         maskEl : false,
11213         
11214         apply : function()
11215         {
11216             if(this.isApplied){
11217                 return;
11218             }
11219             
11220             this.maskEl = {
11221                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11222                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11223                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11224                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11225             };
11226             
11227             this.maskEl.top.enableDisplayMode("block");
11228             this.maskEl.left.enableDisplayMode("block");
11229             this.maskEl.bottom.enableDisplayMode("block");
11230             this.maskEl.right.enableDisplayMode("block");
11231             
11232             this.toolTip = new Roo.bootstrap.Tooltip({
11233                 cls : 'roo-form-error-popover',
11234                 alignment : {
11235                     'left' : ['r-l', [-2,0], 'right'],
11236                     'right' : ['l-r', [2,0], 'left'],
11237                     'bottom' : ['tl-bl', [0,2], 'top'],
11238                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11239                 }
11240             });
11241             
11242             this.toolTip.render(Roo.get(document.body));
11243
11244             this.toolTip.el.enableDisplayMode("block");
11245             
11246             Roo.get(document.body).on('click', function(){
11247                 this.unmask();
11248             }, this);
11249             
11250             Roo.get(document.body).on('touchstart', function(){
11251                 this.unmask();
11252             }, this);
11253             
11254             this.isApplied = true
11255         },
11256         
11257         mask : function(form, target)
11258         {
11259             this.form = form;
11260             
11261             this.target = target;
11262             
11263             if(!this.form.errorMask || !target.el){
11264                 return;
11265             }
11266             
11267             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11268             
11269             Roo.log(scrollable);
11270             
11271             var ot = this.target.el.calcOffsetsTo(scrollable);
11272             
11273             var scrollTo = ot[1] - this.form.maskOffset;
11274             
11275             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11276             
11277             scrollable.scrollTo('top', scrollTo);
11278             
11279             var box = this.target.el.getBox();
11280             Roo.log(box);
11281             var zIndex = Roo.bootstrap.Modal.zIndex++;
11282
11283             
11284             this.maskEl.top.setStyle('position', 'absolute');
11285             this.maskEl.top.setStyle('z-index', zIndex);
11286             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11287             this.maskEl.top.setLeft(0);
11288             this.maskEl.top.setTop(0);
11289             this.maskEl.top.show();
11290             
11291             this.maskEl.left.setStyle('position', 'absolute');
11292             this.maskEl.left.setStyle('z-index', zIndex);
11293             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11294             this.maskEl.left.setLeft(0);
11295             this.maskEl.left.setTop(box.y - this.padding);
11296             this.maskEl.left.show();
11297
11298             this.maskEl.bottom.setStyle('position', 'absolute');
11299             this.maskEl.bottom.setStyle('z-index', zIndex);
11300             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11301             this.maskEl.bottom.setLeft(0);
11302             this.maskEl.bottom.setTop(box.bottom + this.padding);
11303             this.maskEl.bottom.show();
11304
11305             this.maskEl.right.setStyle('position', 'absolute');
11306             this.maskEl.right.setStyle('z-index', zIndex);
11307             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11308             this.maskEl.right.setLeft(box.right + this.padding);
11309             this.maskEl.right.setTop(box.y - this.padding);
11310             this.maskEl.right.show();
11311
11312             this.toolTip.bindEl = this.target.el;
11313
11314             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11315
11316             var tip = this.target.blankText;
11317
11318             if(this.target.getValue() !== '' ) {
11319                 
11320                 if (this.target.invalidText.length) {
11321                     tip = this.target.invalidText;
11322                 } else if (this.target.regexText.length){
11323                     tip = this.target.regexText;
11324                 }
11325             }
11326
11327             this.toolTip.show(tip);
11328
11329             this.intervalID = window.setInterval(function() {
11330                 Roo.bootstrap.Form.popover.unmask();
11331             }, 10000);
11332
11333             window.onwheel = function(){ return false;};
11334             
11335             (function(){ this.isMasked = true; }).defer(500, this);
11336             
11337         },
11338         
11339         unmask : function()
11340         {
11341             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11342                 return;
11343             }
11344             
11345             this.maskEl.top.setStyle('position', 'absolute');
11346             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11347             this.maskEl.top.hide();
11348
11349             this.maskEl.left.setStyle('position', 'absolute');
11350             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11351             this.maskEl.left.hide();
11352
11353             this.maskEl.bottom.setStyle('position', 'absolute');
11354             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11355             this.maskEl.bottom.hide();
11356
11357             this.maskEl.right.setStyle('position', 'absolute');
11358             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11359             this.maskEl.right.hide();
11360             
11361             this.toolTip.hide();
11362             
11363             this.toolTip.el.hide();
11364             
11365             window.onwheel = function(){ return true;};
11366             
11367             if(this.intervalID){
11368                 window.clearInterval(this.intervalID);
11369                 this.intervalID = false;
11370             }
11371             
11372             this.isMasked = false;
11373             
11374         }
11375         
11376     }
11377     
11378 });
11379
11380 /*
11381  * Based on:
11382  * Ext JS Library 1.1.1
11383  * Copyright(c) 2006-2007, Ext JS, LLC.
11384  *
11385  * Originally Released Under LGPL - original licence link has changed is not relivant.
11386  *
11387  * Fork - LGPL
11388  * <script type="text/javascript">
11389  */
11390 /**
11391  * @class Roo.form.VTypes
11392  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11393  * @singleton
11394  */
11395 Roo.form.VTypes = function(){
11396     // closure these in so they are only created once.
11397     var alpha = /^[a-zA-Z_]+$/;
11398     var alphanum = /^[a-zA-Z0-9_]+$/;
11399     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11400     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11401
11402     // All these messages and functions are configurable
11403     return {
11404         /**
11405          * The function used to validate email addresses
11406          * @param {String} value The email address
11407          */
11408         'email' : function(v){
11409             return email.test(v);
11410         },
11411         /**
11412          * The error text to display when the email validation function returns false
11413          * @type String
11414          */
11415         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11416         /**
11417          * The keystroke filter mask to be applied on email input
11418          * @type RegExp
11419          */
11420         'emailMask' : /[a-z0-9_\.\-@]/i,
11421
11422         /**
11423          * The function used to validate URLs
11424          * @param {String} value The URL
11425          */
11426         'url' : function(v){
11427             return url.test(v);
11428         },
11429         /**
11430          * The error text to display when the url validation function returns false
11431          * @type String
11432          */
11433         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11434         
11435         /**
11436          * The function used to validate alpha values
11437          * @param {String} value The value
11438          */
11439         'alpha' : function(v){
11440             return alpha.test(v);
11441         },
11442         /**
11443          * The error text to display when the alpha validation function returns false
11444          * @type String
11445          */
11446         'alphaText' : 'This field should only contain letters and _',
11447         /**
11448          * The keystroke filter mask to be applied on alpha input
11449          * @type RegExp
11450          */
11451         'alphaMask' : /[a-z_]/i,
11452
11453         /**
11454          * The function used to validate alphanumeric values
11455          * @param {String} value The value
11456          */
11457         'alphanum' : function(v){
11458             return alphanum.test(v);
11459         },
11460         /**
11461          * The error text to display when the alphanumeric validation function returns false
11462          * @type String
11463          */
11464         'alphanumText' : 'This field should only contain letters, numbers and _',
11465         /**
11466          * The keystroke filter mask to be applied on alphanumeric input
11467          * @type RegExp
11468          */
11469         'alphanumMask' : /[a-z0-9_]/i
11470     };
11471 }();/*
11472  * - LGPL
11473  *
11474  * Input
11475  * 
11476  */
11477
11478 /**
11479  * @class Roo.bootstrap.Input
11480  * @extends Roo.bootstrap.Component
11481  * Bootstrap Input class
11482  * @cfg {Boolean} disabled is it disabled
11483  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11484  * @cfg {String} name name of the input
11485  * @cfg {string} fieldLabel - the label associated
11486  * @cfg {string} placeholder - placeholder to put in text.
11487  * @cfg {string}  before - input group add on before
11488  * @cfg {string} after - input group add on after
11489  * @cfg {string} size - (lg|sm) or leave empty..
11490  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11491  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11492  * @cfg {Number} md colspan out of 12 for computer-sized screens
11493  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11494  * @cfg {string} value default value of the input
11495  * @cfg {Number} labelWidth set the width of label 
11496  * @cfg {Number} labellg set the width of label (1-12)
11497  * @cfg {Number} labelmd set the width of label (1-12)
11498  * @cfg {Number} labelsm set the width of label (1-12)
11499  * @cfg {Number} labelxs set the width of label (1-12)
11500  * @cfg {String} labelAlign (top|left)
11501  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11502  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11503  * @cfg {String} indicatorpos (left|right) default left
11504  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11505  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11506  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11507
11508  * @cfg {String} align (left|center|right) Default left
11509  * @cfg {Boolean} forceFeedback (true|false) Default false
11510  * 
11511  * @constructor
11512  * Create a new Input
11513  * @param {Object} config The config object
11514  */
11515
11516 Roo.bootstrap.Input = function(config){
11517     
11518     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11519     
11520     this.addEvents({
11521         /**
11522          * @event focus
11523          * Fires when this field receives input focus.
11524          * @param {Roo.form.Field} this
11525          */
11526         focus : true,
11527         /**
11528          * @event blur
11529          * Fires when this field loses input focus.
11530          * @param {Roo.form.Field} this
11531          */
11532         blur : true,
11533         /**
11534          * @event specialkey
11535          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11536          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11537          * @param {Roo.form.Field} this
11538          * @param {Roo.EventObject} e The event object
11539          */
11540         specialkey : true,
11541         /**
11542          * @event change
11543          * Fires just before the field blurs if the field value has changed.
11544          * @param {Roo.form.Field} this
11545          * @param {Mixed} newValue The new value
11546          * @param {Mixed} oldValue The original value
11547          */
11548         change : true,
11549         /**
11550          * @event invalid
11551          * Fires after the field has been marked as invalid.
11552          * @param {Roo.form.Field} this
11553          * @param {String} msg The validation message
11554          */
11555         invalid : true,
11556         /**
11557          * @event valid
11558          * Fires after the field has been validated with no errors.
11559          * @param {Roo.form.Field} this
11560          */
11561         valid : true,
11562          /**
11563          * @event keyup
11564          * Fires after the key up
11565          * @param {Roo.form.Field} this
11566          * @param {Roo.EventObject}  e The event Object
11567          */
11568         keyup : true,
11569         /**
11570          * @event paste
11571          * Fires after the user pastes into input
11572          * @param {Roo.form.Field} this
11573          * @param {Roo.EventObject}  e The event Object
11574          */
11575         paste : true
11576     });
11577 };
11578
11579 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11580      /**
11581      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11582       automatic validation (defaults to "keyup").
11583      */
11584     validationEvent : "keyup",
11585      /**
11586      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11587      */
11588     validateOnBlur : true,
11589     /**
11590      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11591      */
11592     validationDelay : 250,
11593      /**
11594      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11595      */
11596     focusClass : "x-form-focus",  // not needed???
11597     
11598        
11599     /**
11600      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11601      */
11602     invalidClass : "has-warning",
11603     
11604     /**
11605      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11606      */
11607     validClass : "has-success",
11608     
11609     /**
11610      * @cfg {Boolean} hasFeedback (true|false) default true
11611      */
11612     hasFeedback : true,
11613     
11614     /**
11615      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11616      */
11617     invalidFeedbackClass : "glyphicon-warning-sign",
11618     
11619     /**
11620      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11621      */
11622     validFeedbackClass : "glyphicon-ok",
11623     
11624     /**
11625      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11626      */
11627     selectOnFocus : false,
11628     
11629      /**
11630      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11631      */
11632     maskRe : null,
11633        /**
11634      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11635      */
11636     vtype : null,
11637     
11638       /**
11639      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11640      */
11641     disableKeyFilter : false,
11642     
11643        /**
11644      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11645      */
11646     disabled : false,
11647      /**
11648      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11649      */
11650     allowBlank : true,
11651     /**
11652      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11653      */
11654     blankText : "Please complete this mandatory field",
11655     
11656      /**
11657      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11658      */
11659     minLength : 0,
11660     /**
11661      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11662      */
11663     maxLength : Number.MAX_VALUE,
11664     /**
11665      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11666      */
11667     minLengthText : "The minimum length for this field is {0}",
11668     /**
11669      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11670      */
11671     maxLengthText : "The maximum length for this field is {0}",
11672   
11673     
11674     /**
11675      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11676      * If available, this function will be called only after the basic validators all return true, and will be passed the
11677      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11678      */
11679     validator : null,
11680     /**
11681      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11682      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11683      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11684      */
11685     regex : null,
11686     /**
11687      * @cfg {String} regexText -- Depricated - use Invalid Text
11688      */
11689     regexText : "",
11690     
11691     /**
11692      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11693      */
11694     invalidText : "",
11695     
11696     
11697     
11698     autocomplete: false,
11699     
11700     
11701     fieldLabel : '',
11702     inputType : 'text',
11703     
11704     name : false,
11705     placeholder: false,
11706     before : false,
11707     after : false,
11708     size : false,
11709     hasFocus : false,
11710     preventMark: false,
11711     isFormField : true,
11712     value : '',
11713     labelWidth : 2,
11714     labelAlign : false,
11715     readOnly : false,
11716     align : false,
11717     formatedValue : false,
11718     forceFeedback : false,
11719     
11720     indicatorpos : 'left',
11721     
11722     labellg : 0,
11723     labelmd : 0,
11724     labelsm : 0,
11725     labelxs : 0,
11726     
11727     capture : '',
11728     accept : '',
11729     
11730     parentLabelAlign : function()
11731     {
11732         var parent = this;
11733         while (parent.parent()) {
11734             parent = parent.parent();
11735             if (typeof(parent.labelAlign) !='undefined') {
11736                 return parent.labelAlign;
11737             }
11738         }
11739         return 'left';
11740         
11741     },
11742     
11743     getAutoCreate : function()
11744     {
11745         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11746         
11747         var id = Roo.id();
11748         
11749         var cfg = {};
11750         
11751         if(this.inputType != 'hidden'){
11752             cfg.cls = 'form-group' //input-group
11753         }
11754         
11755         var input =  {
11756             tag: 'input',
11757             id : id,
11758             type : this.inputType,
11759             value : this.value,
11760             cls : 'form-control',
11761             placeholder : this.placeholder || '',
11762             autocomplete : this.autocomplete || 'new-password'
11763         };
11764         if (this.inputType == 'file') {
11765             input.style = 'overflow:hidden'; // why not in CSS?
11766         }
11767         
11768         if(this.capture.length){
11769             input.capture = this.capture;
11770         }
11771         
11772         if(this.accept.length){
11773             input.accept = this.accept + "/*";
11774         }
11775         
11776         if(this.align){
11777             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11778         }
11779         
11780         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11781             input.maxLength = this.maxLength;
11782         }
11783         
11784         if (this.disabled) {
11785             input.disabled=true;
11786         }
11787         
11788         if (this.readOnly) {
11789             input.readonly=true;
11790         }
11791         
11792         if (this.name) {
11793             input.name = this.name;
11794         }
11795         
11796         if (this.size) {
11797             input.cls += ' input-' + this.size;
11798         }
11799         
11800         var settings=this;
11801         ['xs','sm','md','lg'].map(function(size){
11802             if (settings[size]) {
11803                 cfg.cls += ' col-' + size + '-' + settings[size];
11804             }
11805         });
11806         
11807         var inputblock = input;
11808         
11809         var feedback = {
11810             tag: 'span',
11811             cls: 'glyphicon form-control-feedback'
11812         };
11813             
11814         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11815             
11816             inputblock = {
11817                 cls : 'has-feedback',
11818                 cn :  [
11819                     input,
11820                     feedback
11821                 ] 
11822             };  
11823         }
11824         
11825         if (this.before || this.after) {
11826             
11827             inputblock = {
11828                 cls : 'input-group',
11829                 cn :  [] 
11830             };
11831             
11832             if (this.before && typeof(this.before) == 'string') {
11833                 
11834                 inputblock.cn.push({
11835                     tag :'span',
11836                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11837                     html : this.before
11838                 });
11839             }
11840             if (this.before && typeof(this.before) == 'object') {
11841                 this.before = Roo.factory(this.before);
11842                 
11843                 inputblock.cn.push({
11844                     tag :'span',
11845                     cls : 'roo-input-before input-group-prepend   input-group-' +
11846                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11847                 });
11848             }
11849             
11850             inputblock.cn.push(input);
11851             
11852             if (this.after && typeof(this.after) == 'string') {
11853                 inputblock.cn.push({
11854                     tag :'span',
11855                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11856                     html : this.after
11857                 });
11858             }
11859             if (this.after && typeof(this.after) == 'object') {
11860                 this.after = Roo.factory(this.after);
11861                 
11862                 inputblock.cn.push({
11863                     tag :'span',
11864                     cls : 'roo-input-after input-group-append  input-group-' +
11865                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11866                 });
11867             }
11868             
11869             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11870                 inputblock.cls += ' has-feedback';
11871                 inputblock.cn.push(feedback);
11872             }
11873         };
11874         var indicator = {
11875             tag : 'i',
11876             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11877             tooltip : 'This field is required'
11878         };
11879         if (this.allowBlank ) {
11880             indicator.style = this.allowBlank ? ' display:none' : '';
11881         }
11882         if (align ==='left' && this.fieldLabel.length) {
11883             
11884             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11885             
11886             cfg.cn = [
11887                 indicator,
11888                 {
11889                     tag: 'label',
11890                     'for' :  id,
11891                     cls : 'control-label col-form-label',
11892                     html : this.fieldLabel
11893
11894                 },
11895                 {
11896                     cls : "", 
11897                     cn: [
11898                         inputblock
11899                     ]
11900                 }
11901             ];
11902             
11903             var labelCfg = cfg.cn[1];
11904             var contentCfg = cfg.cn[2];
11905             
11906             if(this.indicatorpos == 'right'){
11907                 cfg.cn = [
11908                     {
11909                         tag: 'label',
11910                         'for' :  id,
11911                         cls : 'control-label col-form-label',
11912                         cn : [
11913                             {
11914                                 tag : 'span',
11915                                 html : this.fieldLabel
11916                             },
11917                             indicator
11918                         ]
11919                     },
11920                     {
11921                         cls : "",
11922                         cn: [
11923                             inputblock
11924                         ]
11925                     }
11926
11927                 ];
11928                 
11929                 labelCfg = cfg.cn[0];
11930                 contentCfg = cfg.cn[1];
11931             
11932             }
11933             
11934             if(this.labelWidth > 12){
11935                 labelCfg.style = "width: " + this.labelWidth + 'px';
11936             }
11937             
11938             if(this.labelWidth < 13 && this.labelmd == 0){
11939                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11940             }
11941             
11942             if(this.labellg > 0){
11943                 labelCfg.cls += ' col-lg-' + this.labellg;
11944                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11945             }
11946             
11947             if(this.labelmd > 0){
11948                 labelCfg.cls += ' col-md-' + this.labelmd;
11949                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11950             }
11951             
11952             if(this.labelsm > 0){
11953                 labelCfg.cls += ' col-sm-' + this.labelsm;
11954                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11955             }
11956             
11957             if(this.labelxs > 0){
11958                 labelCfg.cls += ' col-xs-' + this.labelxs;
11959                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11960             }
11961             
11962             
11963         } else if ( this.fieldLabel.length) {
11964                 
11965             
11966             
11967             cfg.cn = [
11968                 {
11969                     tag : 'i',
11970                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11971                     tooltip : 'This field is required',
11972                     style : this.allowBlank ? ' display:none' : '' 
11973                 },
11974                 {
11975                     tag: 'label',
11976                    //cls : 'input-group-addon',
11977                     html : this.fieldLabel
11978
11979                 },
11980
11981                inputblock
11982
11983            ];
11984            
11985            if(this.indicatorpos == 'right'){
11986        
11987                 cfg.cn = [
11988                     {
11989                         tag: 'label',
11990                        //cls : 'input-group-addon',
11991                         html : this.fieldLabel
11992
11993                     },
11994                     {
11995                         tag : 'i',
11996                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11997                         tooltip : 'This field is required',
11998                         style : this.allowBlank ? ' display:none' : '' 
11999                     },
12000
12001                    inputblock
12002
12003                ];
12004
12005             }
12006
12007         } else {
12008             
12009             cfg.cn = [
12010
12011                     inputblock
12012
12013             ];
12014                 
12015                 
12016         };
12017         
12018         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12019            cfg.cls += ' navbar-form';
12020         }
12021         
12022         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12023             // on BS4 we do this only if not form 
12024             cfg.cls += ' navbar-form';
12025             cfg.tag = 'li';
12026         }
12027         
12028         return cfg;
12029         
12030     },
12031     /**
12032      * return the real input element.
12033      */
12034     inputEl: function ()
12035     {
12036         return this.el.select('input.form-control',true).first();
12037     },
12038     
12039     tooltipEl : function()
12040     {
12041         return this.inputEl();
12042     },
12043     
12044     indicatorEl : function()
12045     {
12046         if (Roo.bootstrap.version == 4) {
12047             return false; // not enabled in v4 yet.
12048         }
12049         
12050         var indicator = this.el.select('i.roo-required-indicator',true).first();
12051         
12052         if(!indicator){
12053             return false;
12054         }
12055         
12056         return indicator;
12057         
12058     },
12059     
12060     setDisabled : function(v)
12061     {
12062         var i  = this.inputEl().dom;
12063         if (!v) {
12064             i.removeAttribute('disabled');
12065             return;
12066             
12067         }
12068         i.setAttribute('disabled','true');
12069     },
12070     initEvents : function()
12071     {
12072           
12073         this.inputEl().on("keydown" , this.fireKey,  this);
12074         this.inputEl().on("focus", this.onFocus,  this);
12075         this.inputEl().on("blur", this.onBlur,  this);
12076         
12077         this.inputEl().relayEvent('keyup', this);
12078         this.inputEl().relayEvent('paste', this);
12079         
12080         this.indicator = this.indicatorEl();
12081         
12082         if(this.indicator){
12083             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12084         }
12085  
12086         // reference to original value for reset
12087         this.originalValue = this.getValue();
12088         //Roo.form.TextField.superclass.initEvents.call(this);
12089         if(this.validationEvent == 'keyup'){
12090             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12091             this.inputEl().on('keyup', this.filterValidation, this);
12092         }
12093         else if(this.validationEvent !== false){
12094             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12095         }
12096         
12097         if(this.selectOnFocus){
12098             this.on("focus", this.preFocus, this);
12099             
12100         }
12101         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12102             this.inputEl().on("keypress", this.filterKeys, this);
12103         } else {
12104             this.inputEl().relayEvent('keypress', this);
12105         }
12106        /* if(this.grow){
12107             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12108             this.el.on("click", this.autoSize,  this);
12109         }
12110         */
12111         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12112             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12113         }
12114         
12115         if (typeof(this.before) == 'object') {
12116             this.before.render(this.el.select('.roo-input-before',true).first());
12117         }
12118         if (typeof(this.after) == 'object') {
12119             this.after.render(this.el.select('.roo-input-after',true).first());
12120         }
12121         
12122         this.inputEl().on('change', this.onChange, this);
12123         
12124     },
12125     filterValidation : function(e){
12126         if(!e.isNavKeyPress()){
12127             this.validationTask.delay(this.validationDelay);
12128         }
12129     },
12130      /**
12131      * Validates the field value
12132      * @return {Boolean} True if the value is valid, else false
12133      */
12134     validate : function(){
12135         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12136         if(this.disabled || this.validateValue(this.getRawValue())){
12137             this.markValid();
12138             return true;
12139         }
12140         
12141         this.markInvalid();
12142         return false;
12143     },
12144     
12145     
12146     /**
12147      * Validates a value according to the field's validation rules and marks the field as invalid
12148      * if the validation fails
12149      * @param {Mixed} value The value to validate
12150      * @return {Boolean} True if the value is valid, else false
12151      */
12152     validateValue : function(value)
12153     {
12154         if(this.getVisibilityEl().hasClass('hidden')){
12155             return true;
12156         }
12157         
12158         if(value.length < 1)  { // if it's blank
12159             if(this.allowBlank){
12160                 return true;
12161             }
12162             return false;
12163         }
12164         
12165         if(value.length < this.minLength){
12166             return false;
12167         }
12168         if(value.length > this.maxLength){
12169             return false;
12170         }
12171         if(this.vtype){
12172             var vt = Roo.form.VTypes;
12173             if(!vt[this.vtype](value, this)){
12174                 return false;
12175             }
12176         }
12177         if(typeof this.validator == "function"){
12178             var msg = this.validator(value);
12179             if(msg !== true){
12180                 return false;
12181             }
12182             if (typeof(msg) == 'string') {
12183                 this.invalidText = msg;
12184             }
12185         }
12186         
12187         if(this.regex && !this.regex.test(value)){
12188             return false;
12189         }
12190         
12191         return true;
12192     },
12193     
12194      // private
12195     fireKey : function(e){
12196         //Roo.log('field ' + e.getKey());
12197         if(e.isNavKeyPress()){
12198             this.fireEvent("specialkey", this, e);
12199         }
12200     },
12201     focus : function (selectText){
12202         if(this.rendered){
12203             this.inputEl().focus();
12204             if(selectText === true){
12205                 this.inputEl().dom.select();
12206             }
12207         }
12208         return this;
12209     } ,
12210     
12211     onFocus : function(){
12212         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12213            // this.el.addClass(this.focusClass);
12214         }
12215         if(!this.hasFocus){
12216             this.hasFocus = true;
12217             this.startValue = this.getValue();
12218             this.fireEvent("focus", this);
12219         }
12220     },
12221     
12222     beforeBlur : Roo.emptyFn,
12223
12224     
12225     // private
12226     onBlur : function(){
12227         this.beforeBlur();
12228         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12229             //this.el.removeClass(this.focusClass);
12230         }
12231         this.hasFocus = false;
12232         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12233             this.validate();
12234         }
12235         var v = this.getValue();
12236         if(String(v) !== String(this.startValue)){
12237             this.fireEvent('change', this, v, this.startValue);
12238         }
12239         this.fireEvent("blur", this);
12240     },
12241     
12242     onChange : function(e)
12243     {
12244         var v = this.getValue();
12245         if(String(v) !== String(this.startValue)){
12246             this.fireEvent('change', this, v, this.startValue);
12247         }
12248         
12249     },
12250     
12251     /**
12252      * Resets the current field value to the originally loaded value and clears any validation messages
12253      */
12254     reset : function(){
12255         this.setValue(this.originalValue);
12256         this.validate();
12257     },
12258      /**
12259      * Returns the name of the field
12260      * @return {Mixed} name The name field
12261      */
12262     getName: function(){
12263         return this.name;
12264     },
12265      /**
12266      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12267      * @return {Mixed} value The field value
12268      */
12269     getValue : function(){
12270         
12271         var v = this.inputEl().getValue();
12272         
12273         return v;
12274     },
12275     /**
12276      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12277      * @return {Mixed} value The field value
12278      */
12279     getRawValue : function(){
12280         var v = this.inputEl().getValue();
12281         
12282         return v;
12283     },
12284     
12285     /**
12286      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12287      * @param {Mixed} value The value to set
12288      */
12289     setRawValue : function(v){
12290         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12291     },
12292     
12293     selectText : function(start, end){
12294         var v = this.getRawValue();
12295         if(v.length > 0){
12296             start = start === undefined ? 0 : start;
12297             end = end === undefined ? v.length : end;
12298             var d = this.inputEl().dom;
12299             if(d.setSelectionRange){
12300                 d.setSelectionRange(start, end);
12301             }else if(d.createTextRange){
12302                 var range = d.createTextRange();
12303                 range.moveStart("character", start);
12304                 range.moveEnd("character", v.length-end);
12305                 range.select();
12306             }
12307         }
12308     },
12309     
12310     /**
12311      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12312      * @param {Mixed} value The value to set
12313      */
12314     setValue : function(v){
12315         this.value = v;
12316         if(this.rendered){
12317             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12318             this.validate();
12319         }
12320     },
12321     
12322     /*
12323     processValue : function(value){
12324         if(this.stripCharsRe){
12325             var newValue = value.replace(this.stripCharsRe, '');
12326             if(newValue !== value){
12327                 this.setRawValue(newValue);
12328                 return newValue;
12329             }
12330         }
12331         return value;
12332     },
12333   */
12334     preFocus : function(){
12335         
12336         if(this.selectOnFocus){
12337             this.inputEl().dom.select();
12338         }
12339     },
12340     filterKeys : function(e){
12341         var k = e.getKey();
12342         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12343             return;
12344         }
12345         var c = e.getCharCode(), cc = String.fromCharCode(c);
12346         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12347             return;
12348         }
12349         if(!this.maskRe.test(cc)){
12350             e.stopEvent();
12351         }
12352     },
12353      /**
12354      * Clear any invalid styles/messages for this field
12355      */
12356     clearInvalid : function(){
12357         
12358         if(!this.el || this.preventMark){ // not rendered
12359             return;
12360         }
12361         
12362         
12363         this.el.removeClass([this.invalidClass, 'is-invalid']);
12364         
12365         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12366             
12367             var feedback = this.el.select('.form-control-feedback', true).first();
12368             
12369             if(feedback){
12370                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12371             }
12372             
12373         }
12374         
12375         if(this.indicator){
12376             this.indicator.removeClass('visible');
12377             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12378         }
12379         
12380         this.fireEvent('valid', this);
12381     },
12382     
12383      /**
12384      * Mark this field as valid
12385      */
12386     markValid : function()
12387     {
12388         if(!this.el  || this.preventMark){ // not rendered...
12389             return;
12390         }
12391         
12392         this.el.removeClass([this.invalidClass, this.validClass]);
12393         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12394
12395         var feedback = this.el.select('.form-control-feedback', true).first();
12396             
12397         if(feedback){
12398             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12399         }
12400         
12401         if(this.indicator){
12402             this.indicator.removeClass('visible');
12403             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12404         }
12405         
12406         if(this.disabled){
12407             return;
12408         }
12409         
12410            
12411         if(this.allowBlank && !this.getRawValue().length){
12412             return;
12413         }
12414         if (Roo.bootstrap.version == 3) {
12415             this.el.addClass(this.validClass);
12416         } else {
12417             this.inputEl().addClass('is-valid');
12418         }
12419
12420         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12421             
12422             var feedback = this.el.select('.form-control-feedback', true).first();
12423             
12424             if(feedback){
12425                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12426                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12427             }
12428             
12429         }
12430         
12431         this.fireEvent('valid', this);
12432     },
12433     
12434      /**
12435      * Mark this field as invalid
12436      * @param {String} msg The validation message
12437      */
12438     markInvalid : function(msg)
12439     {
12440         if(!this.el  || this.preventMark){ // not rendered
12441             return;
12442         }
12443         
12444         this.el.removeClass([this.invalidClass, this.validClass]);
12445         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12446         
12447         var feedback = this.el.select('.form-control-feedback', true).first();
12448             
12449         if(feedback){
12450             this.el.select('.form-control-feedback', true).first().removeClass(
12451                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12452         }
12453
12454         if(this.disabled){
12455             return;
12456         }
12457         
12458         if(this.allowBlank && !this.getRawValue().length){
12459             return;
12460         }
12461         
12462         if(this.indicator){
12463             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12464             this.indicator.addClass('visible');
12465         }
12466         if (Roo.bootstrap.version == 3) {
12467             this.el.addClass(this.invalidClass);
12468         } else {
12469             this.inputEl().addClass('is-invalid');
12470         }
12471         
12472         
12473         
12474         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12475             
12476             var feedback = this.el.select('.form-control-feedback', true).first();
12477             
12478             if(feedback){
12479                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12480                 
12481                 if(this.getValue().length || this.forceFeedback){
12482                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12483                 }
12484                 
12485             }
12486             
12487         }
12488         
12489         this.fireEvent('invalid', this, msg);
12490     },
12491     // private
12492     SafariOnKeyDown : function(event)
12493     {
12494         // this is a workaround for a password hang bug on chrome/ webkit.
12495         if (this.inputEl().dom.type != 'password') {
12496             return;
12497         }
12498         
12499         var isSelectAll = false;
12500         
12501         if(this.inputEl().dom.selectionEnd > 0){
12502             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12503         }
12504         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12505             event.preventDefault();
12506             this.setValue('');
12507             return;
12508         }
12509         
12510         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12511             
12512             event.preventDefault();
12513             // this is very hacky as keydown always get's upper case.
12514             //
12515             var cc = String.fromCharCode(event.getCharCode());
12516             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12517             
12518         }
12519     },
12520     adjustWidth : function(tag, w){
12521         tag = tag.toLowerCase();
12522         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12523             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12524                 if(tag == 'input'){
12525                     return w + 2;
12526                 }
12527                 if(tag == 'textarea'){
12528                     return w-2;
12529                 }
12530             }else if(Roo.isOpera){
12531                 if(tag == 'input'){
12532                     return w + 2;
12533                 }
12534                 if(tag == 'textarea'){
12535                     return w-2;
12536                 }
12537             }
12538         }
12539         return w;
12540     },
12541     
12542     setFieldLabel : function(v)
12543     {
12544         if(!this.rendered){
12545             return;
12546         }
12547         
12548         if(this.indicatorEl()){
12549             var ar = this.el.select('label > span',true);
12550             
12551             if (ar.elements.length) {
12552                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12553                 this.fieldLabel = v;
12554                 return;
12555             }
12556             
12557             var br = this.el.select('label',true);
12558             
12559             if(br.elements.length) {
12560                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12561                 this.fieldLabel = v;
12562                 return;
12563             }
12564             
12565             Roo.log('Cannot Found any of label > span || label in input');
12566             return;
12567         }
12568         
12569         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12570         this.fieldLabel = v;
12571         
12572         
12573     }
12574 });
12575
12576  
12577 /*
12578  * - LGPL
12579  *
12580  * Input
12581  * 
12582  */
12583
12584 /**
12585  * @class Roo.bootstrap.TextArea
12586  * @extends Roo.bootstrap.Input
12587  * Bootstrap TextArea class
12588  * @cfg {Number} cols Specifies the visible width of a text area
12589  * @cfg {Number} rows Specifies the visible number of lines in a text area
12590  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12591  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12592  * @cfg {string} html text
12593  * 
12594  * @constructor
12595  * Create a new TextArea
12596  * @param {Object} config The config object
12597  */
12598
12599 Roo.bootstrap.TextArea = function(config){
12600     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12601    
12602 };
12603
12604 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12605      
12606     cols : false,
12607     rows : 5,
12608     readOnly : false,
12609     warp : 'soft',
12610     resize : false,
12611     value: false,
12612     html: false,
12613     
12614     getAutoCreate : function(){
12615         
12616         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12617         
12618         var id = Roo.id();
12619         
12620         var cfg = {};
12621         
12622         if(this.inputType != 'hidden'){
12623             cfg.cls = 'form-group' //input-group
12624         }
12625         
12626         var input =  {
12627             tag: 'textarea',
12628             id : id,
12629             warp : this.warp,
12630             rows : this.rows,
12631             value : this.value || '',
12632             html: this.html || '',
12633             cls : 'form-control',
12634             placeholder : this.placeholder || '' 
12635             
12636         };
12637         
12638         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12639             input.maxLength = this.maxLength;
12640         }
12641         
12642         if(this.resize){
12643             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12644         }
12645         
12646         if(this.cols){
12647             input.cols = this.cols;
12648         }
12649         
12650         if (this.readOnly) {
12651             input.readonly = true;
12652         }
12653         
12654         if (this.name) {
12655             input.name = this.name;
12656         }
12657         
12658         if (this.size) {
12659             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12660         }
12661         
12662         var settings=this;
12663         ['xs','sm','md','lg'].map(function(size){
12664             if (settings[size]) {
12665                 cfg.cls += ' col-' + size + '-' + settings[size];
12666             }
12667         });
12668         
12669         var inputblock = input;
12670         
12671         if(this.hasFeedback && !this.allowBlank){
12672             
12673             var feedback = {
12674                 tag: 'span',
12675                 cls: 'glyphicon form-control-feedback'
12676             };
12677
12678             inputblock = {
12679                 cls : 'has-feedback',
12680                 cn :  [
12681                     input,
12682                     feedback
12683                 ] 
12684             };  
12685         }
12686         
12687         
12688         if (this.before || this.after) {
12689             
12690             inputblock = {
12691                 cls : 'input-group',
12692                 cn :  [] 
12693             };
12694             if (this.before) {
12695                 inputblock.cn.push({
12696                     tag :'span',
12697                     cls : 'input-group-addon',
12698                     html : this.before
12699                 });
12700             }
12701             
12702             inputblock.cn.push(input);
12703             
12704             if(this.hasFeedback && !this.allowBlank){
12705                 inputblock.cls += ' has-feedback';
12706                 inputblock.cn.push(feedback);
12707             }
12708             
12709             if (this.after) {
12710                 inputblock.cn.push({
12711                     tag :'span',
12712                     cls : 'input-group-addon',
12713                     html : this.after
12714                 });
12715             }
12716             
12717         }
12718         
12719         if (align ==='left' && this.fieldLabel.length) {
12720             cfg.cn = [
12721                 {
12722                     tag: 'label',
12723                     'for' :  id,
12724                     cls : 'control-label',
12725                     html : this.fieldLabel
12726                 },
12727                 {
12728                     cls : "",
12729                     cn: [
12730                         inputblock
12731                     ]
12732                 }
12733
12734             ];
12735             
12736             if(this.labelWidth > 12){
12737                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12738             }
12739
12740             if(this.labelWidth < 13 && this.labelmd == 0){
12741                 this.labelmd = this.labelWidth;
12742             }
12743
12744             if(this.labellg > 0){
12745                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12746                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12747             }
12748
12749             if(this.labelmd > 0){
12750                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12751                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12752             }
12753
12754             if(this.labelsm > 0){
12755                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12756                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12757             }
12758
12759             if(this.labelxs > 0){
12760                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12761                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12762             }
12763             
12764         } else if ( this.fieldLabel.length) {
12765             cfg.cn = [
12766
12767                {
12768                    tag: 'label',
12769                    //cls : 'input-group-addon',
12770                    html : this.fieldLabel
12771
12772                },
12773
12774                inputblock
12775
12776            ];
12777
12778         } else {
12779
12780             cfg.cn = [
12781
12782                 inputblock
12783
12784             ];
12785                 
12786         }
12787         
12788         if (this.disabled) {
12789             input.disabled=true;
12790         }
12791         
12792         return cfg;
12793         
12794     },
12795     /**
12796      * return the real textarea element.
12797      */
12798     inputEl: function ()
12799     {
12800         return this.el.select('textarea.form-control',true).first();
12801     },
12802     
12803     /**
12804      * Clear any invalid styles/messages for this field
12805      */
12806     clearInvalid : function()
12807     {
12808         
12809         if(!this.el || this.preventMark){ // not rendered
12810             return;
12811         }
12812         
12813         var label = this.el.select('label', true).first();
12814         var icon = this.el.select('i.fa-star', true).first();
12815         
12816         if(label && icon){
12817             icon.remove();
12818         }
12819         this.el.removeClass( this.validClass);
12820         this.inputEl().removeClass('is-invalid');
12821          
12822         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12823             
12824             var feedback = this.el.select('.form-control-feedback', true).first();
12825             
12826             if(feedback){
12827                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12828             }
12829             
12830         }
12831         
12832         this.fireEvent('valid', this);
12833     },
12834     
12835      /**
12836      * Mark this field as valid
12837      */
12838     markValid : function()
12839     {
12840         if(!this.el  || this.preventMark){ // not rendered
12841             return;
12842         }
12843         
12844         this.el.removeClass([this.invalidClass, this.validClass]);
12845         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12846         
12847         var feedback = this.el.select('.form-control-feedback', true).first();
12848             
12849         if(feedback){
12850             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12851         }
12852
12853         if(this.disabled || this.allowBlank){
12854             return;
12855         }
12856         
12857         var label = this.el.select('label', true).first();
12858         var icon = this.el.select('i.fa-star', true).first();
12859         
12860         if(label && icon){
12861             icon.remove();
12862         }
12863         if (Roo.bootstrap.version == 3) {
12864             this.el.addClass(this.validClass);
12865         } else {
12866             this.inputEl().addClass('is-valid');
12867         }
12868         
12869         
12870         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12871             
12872             var feedback = this.el.select('.form-control-feedback', true).first();
12873             
12874             if(feedback){
12875                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12876                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12877             }
12878             
12879         }
12880         
12881         this.fireEvent('valid', this);
12882     },
12883     
12884      /**
12885      * Mark this field as invalid
12886      * @param {String} msg The validation message
12887      */
12888     markInvalid : function(msg)
12889     {
12890         if(!this.el  || this.preventMark){ // not rendered
12891             return;
12892         }
12893         
12894         this.el.removeClass([this.invalidClass, this.validClass]);
12895         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12896         
12897         var feedback = this.el.select('.form-control-feedback', true).first();
12898             
12899         if(feedback){
12900             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12901         }
12902
12903         if(this.disabled || this.allowBlank){
12904             return;
12905         }
12906         
12907         var label = this.el.select('label', true).first();
12908         var icon = this.el.select('i.fa-star', true).first();
12909         
12910         if(!this.getValue().length && label && !icon){
12911             this.el.createChild({
12912                 tag : 'i',
12913                 cls : 'text-danger fa fa-lg fa-star',
12914                 tooltip : 'This field is required',
12915                 style : 'margin-right:5px;'
12916             }, label, true);
12917         }
12918         
12919         if (Roo.bootstrap.version == 3) {
12920             this.el.addClass(this.invalidClass);
12921         } else {
12922             this.inputEl().addClass('is-invalid');
12923         }
12924         
12925         // fixme ... this may be depricated need to test..
12926         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12927             
12928             var feedback = this.el.select('.form-control-feedback', true).first();
12929             
12930             if(feedback){
12931                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12932                 
12933                 if(this.getValue().length || this.forceFeedback){
12934                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12935                 }
12936                 
12937             }
12938             
12939         }
12940         
12941         this.fireEvent('invalid', this, msg);
12942     }
12943 });
12944
12945  
12946 /*
12947  * - LGPL
12948  *
12949  * trigger field - base class for combo..
12950  * 
12951  */
12952  
12953 /**
12954  * @class Roo.bootstrap.TriggerField
12955  * @extends Roo.bootstrap.Input
12956  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12957  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12958  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12959  * for which you can provide a custom implementation.  For example:
12960  * <pre><code>
12961 var trigger = new Roo.bootstrap.TriggerField();
12962 trigger.onTriggerClick = myTriggerFn;
12963 trigger.applyTo('my-field');
12964 </code></pre>
12965  *
12966  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12967  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12968  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12969  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12970  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12971
12972  * @constructor
12973  * Create a new TriggerField.
12974  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12975  * to the base TextField)
12976  */
12977 Roo.bootstrap.TriggerField = function(config){
12978     this.mimicing = false;
12979     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12980 };
12981
12982 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12983     /**
12984      * @cfg {String} triggerClass A CSS class to apply to the trigger
12985      */
12986      /**
12987      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12988      */
12989     hideTrigger:false,
12990
12991     /**
12992      * @cfg {Boolean} removable (true|false) special filter default false
12993      */
12994     removable : false,
12995     
12996     /** @cfg {Boolean} grow @hide */
12997     /** @cfg {Number} growMin @hide */
12998     /** @cfg {Number} growMax @hide */
12999
13000     /**
13001      * @hide 
13002      * @method
13003      */
13004     autoSize: Roo.emptyFn,
13005     // private
13006     monitorTab : true,
13007     // private
13008     deferHeight : true,
13009
13010     
13011     actionMode : 'wrap',
13012     
13013     caret : false,
13014     
13015     
13016     getAutoCreate : function(){
13017        
13018         var align = this.labelAlign || this.parentLabelAlign();
13019         
13020         var id = Roo.id();
13021         
13022         var cfg = {
13023             cls: 'form-group' //input-group
13024         };
13025         
13026         
13027         var input =  {
13028             tag: 'input',
13029             id : id,
13030             type : this.inputType,
13031             cls : 'form-control',
13032             autocomplete: 'new-password',
13033             placeholder : this.placeholder || '' 
13034             
13035         };
13036         if (this.name) {
13037             input.name = this.name;
13038         }
13039         if (this.size) {
13040             input.cls += ' input-' + this.size;
13041         }
13042         
13043         if (this.disabled) {
13044             input.disabled=true;
13045         }
13046         
13047         var inputblock = input;
13048         
13049         if(this.hasFeedback && !this.allowBlank){
13050             
13051             var feedback = {
13052                 tag: 'span',
13053                 cls: 'glyphicon form-control-feedback'
13054             };
13055             
13056             if(this.removable && !this.editable  ){
13057                 inputblock = {
13058                     cls : 'has-feedback',
13059                     cn :  [
13060                         inputblock,
13061                         {
13062                             tag: 'button',
13063                             html : 'x',
13064                             cls : 'roo-combo-removable-btn close'
13065                         },
13066                         feedback
13067                     ] 
13068                 };
13069             } else {
13070                 inputblock = {
13071                     cls : 'has-feedback',
13072                     cn :  [
13073                         inputblock,
13074                         feedback
13075                     ] 
13076                 };
13077             }
13078
13079         } else {
13080             if(this.removable && !this.editable ){
13081                 inputblock = {
13082                     cls : 'roo-removable',
13083                     cn :  [
13084                         inputblock,
13085                         {
13086                             tag: 'button',
13087                             html : 'x',
13088                             cls : 'roo-combo-removable-btn close'
13089                         }
13090                     ] 
13091                 };
13092             }
13093         }
13094         
13095         if (this.before || this.after) {
13096             
13097             inputblock = {
13098                 cls : 'input-group',
13099                 cn :  [] 
13100             };
13101             if (this.before) {
13102                 inputblock.cn.push({
13103                     tag :'span',
13104                     cls : 'input-group-addon input-group-prepend input-group-text',
13105                     html : this.before
13106                 });
13107             }
13108             
13109             inputblock.cn.push(input);
13110             
13111             if(this.hasFeedback && !this.allowBlank){
13112                 inputblock.cls += ' has-feedback';
13113                 inputblock.cn.push(feedback);
13114             }
13115             
13116             if (this.after) {
13117                 inputblock.cn.push({
13118                     tag :'span',
13119                     cls : 'input-group-addon input-group-append input-group-text',
13120                     html : this.after
13121                 });
13122             }
13123             
13124         };
13125         
13126       
13127         
13128         var ibwrap = inputblock;
13129         
13130         if(this.multiple){
13131             ibwrap = {
13132                 tag: 'ul',
13133                 cls: 'roo-select2-choices',
13134                 cn:[
13135                     {
13136                         tag: 'li',
13137                         cls: 'roo-select2-search-field',
13138                         cn: [
13139
13140                             inputblock
13141                         ]
13142                     }
13143                 ]
13144             };
13145                 
13146         }
13147         
13148         var combobox = {
13149             cls: 'roo-select2-container input-group',
13150             cn: [
13151                  {
13152                     tag: 'input',
13153                     type : 'hidden',
13154                     cls: 'form-hidden-field'
13155                 },
13156                 ibwrap
13157             ]
13158         };
13159         
13160         if(!this.multiple && this.showToggleBtn){
13161             
13162             var caret = {
13163                         tag: 'span',
13164                         cls: 'caret'
13165              };
13166             if (this.caret != false) {
13167                 caret = {
13168                      tag: 'i',
13169                      cls: 'fa fa-' + this.caret
13170                 };
13171                 
13172             }
13173             
13174             combobox.cn.push({
13175                 tag :'span',
13176                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13177                 cn : [
13178                     Roo.bootstrap.version == 3 ? caret : '',
13179                     {
13180                         tag: 'span',
13181                         cls: 'combobox-clear',
13182                         cn  : [
13183                             {
13184                                 tag : 'i',
13185                                 cls: 'icon-remove'
13186                             }
13187                         ]
13188                     }
13189                 ]
13190
13191             })
13192         }
13193         
13194         if(this.multiple){
13195             combobox.cls += ' roo-select2-container-multi';
13196         }
13197          var indicator = {
13198             tag : 'i',
13199             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13200             tooltip : 'This field is required'
13201         };
13202         if (Roo.bootstrap.version == 4) {
13203             indicator = {
13204                 tag : 'i',
13205                 style : 'display:none'
13206             };
13207         }
13208         
13209         
13210         if (align ==='left' && this.fieldLabel.length) {
13211             
13212             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13213
13214             cfg.cn = [
13215                 indicator,
13216                 {
13217                     tag: 'label',
13218                     'for' :  id,
13219                     cls : 'control-label',
13220                     html : this.fieldLabel
13221
13222                 },
13223                 {
13224                     cls : "", 
13225                     cn: [
13226                         combobox
13227                     ]
13228                 }
13229
13230             ];
13231             
13232             var labelCfg = cfg.cn[1];
13233             var contentCfg = cfg.cn[2];
13234             
13235             if(this.indicatorpos == 'right'){
13236                 cfg.cn = [
13237                     {
13238                         tag: 'label',
13239                         'for' :  id,
13240                         cls : 'control-label',
13241                         cn : [
13242                             {
13243                                 tag : 'span',
13244                                 html : this.fieldLabel
13245                             },
13246                             indicator
13247                         ]
13248                     },
13249                     {
13250                         cls : "", 
13251                         cn: [
13252                             combobox
13253                         ]
13254                     }
13255
13256                 ];
13257                 
13258                 labelCfg = cfg.cn[0];
13259                 contentCfg = cfg.cn[1];
13260             }
13261             
13262             if(this.labelWidth > 12){
13263                 labelCfg.style = "width: " + this.labelWidth + 'px';
13264             }
13265             
13266             if(this.labelWidth < 13 && this.labelmd == 0){
13267                 this.labelmd = this.labelWidth;
13268             }
13269             
13270             if(this.labellg > 0){
13271                 labelCfg.cls += ' col-lg-' + this.labellg;
13272                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13273             }
13274             
13275             if(this.labelmd > 0){
13276                 labelCfg.cls += ' col-md-' + this.labelmd;
13277                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13278             }
13279             
13280             if(this.labelsm > 0){
13281                 labelCfg.cls += ' col-sm-' + this.labelsm;
13282                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13283             }
13284             
13285             if(this.labelxs > 0){
13286                 labelCfg.cls += ' col-xs-' + this.labelxs;
13287                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13288             }
13289             
13290         } else if ( this.fieldLabel.length) {
13291 //                Roo.log(" label");
13292             cfg.cn = [
13293                 indicator,
13294                {
13295                    tag: 'label',
13296                    //cls : 'input-group-addon',
13297                    html : this.fieldLabel
13298
13299                },
13300
13301                combobox
13302
13303             ];
13304             
13305             if(this.indicatorpos == 'right'){
13306                 
13307                 cfg.cn = [
13308                     {
13309                        tag: 'label',
13310                        cn : [
13311                            {
13312                                tag : 'span',
13313                                html : this.fieldLabel
13314                            },
13315                            indicator
13316                        ]
13317
13318                     },
13319                     combobox
13320
13321                 ];
13322
13323             }
13324
13325         } else {
13326             
13327 //                Roo.log(" no label && no align");
13328                 cfg = combobox
13329                      
13330                 
13331         }
13332         
13333         var settings=this;
13334         ['xs','sm','md','lg'].map(function(size){
13335             if (settings[size]) {
13336                 cfg.cls += ' col-' + size + '-' + settings[size];
13337             }
13338         });
13339         
13340         return cfg;
13341         
13342     },
13343     
13344     
13345     
13346     // private
13347     onResize : function(w, h){
13348 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13349 //        if(typeof w == 'number'){
13350 //            var x = w - this.trigger.getWidth();
13351 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13352 //            this.trigger.setStyle('left', x+'px');
13353 //        }
13354     },
13355
13356     // private
13357     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13358
13359     // private
13360     getResizeEl : function(){
13361         return this.inputEl();
13362     },
13363
13364     // private
13365     getPositionEl : function(){
13366         return this.inputEl();
13367     },
13368
13369     // private
13370     alignErrorIcon : function(){
13371         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13372     },
13373
13374     // private
13375     initEvents : function(){
13376         
13377         this.createList();
13378         
13379         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13380         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13381         if(!this.multiple && this.showToggleBtn){
13382             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13383             if(this.hideTrigger){
13384                 this.trigger.setDisplayed(false);
13385             }
13386             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13387         }
13388         
13389         if(this.multiple){
13390             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13391         }
13392         
13393         if(this.removable && !this.editable && !this.tickable){
13394             var close = this.closeTriggerEl();
13395             
13396             if(close){
13397                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13398                 close.on('click', this.removeBtnClick, this, close);
13399             }
13400         }
13401         
13402         //this.trigger.addClassOnOver('x-form-trigger-over');
13403         //this.trigger.addClassOnClick('x-form-trigger-click');
13404         
13405         //if(!this.width){
13406         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13407         //}
13408     },
13409     
13410     closeTriggerEl : function()
13411     {
13412         var close = this.el.select('.roo-combo-removable-btn', true).first();
13413         return close ? close : false;
13414     },
13415     
13416     removeBtnClick : function(e, h, el)
13417     {
13418         e.preventDefault();
13419         
13420         if(this.fireEvent("remove", this) !== false){
13421             this.reset();
13422             this.fireEvent("afterremove", this)
13423         }
13424     },
13425     
13426     createList : function()
13427     {
13428         this.list = Roo.get(document.body).createChild({
13429             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13430             cls: 'typeahead typeahead-long dropdown-menu shadow',
13431             style: 'display:none'
13432         });
13433         
13434         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13435         
13436     },
13437
13438     // private
13439     initTrigger : function(){
13440        
13441     },
13442
13443     // private
13444     onDestroy : function(){
13445         if(this.trigger){
13446             this.trigger.removeAllListeners();
13447           //  this.trigger.remove();
13448         }
13449         //if(this.wrap){
13450         //    this.wrap.remove();
13451         //}
13452         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13453     },
13454
13455     // private
13456     onFocus : function(){
13457         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13458         /*
13459         if(!this.mimicing){
13460             this.wrap.addClass('x-trigger-wrap-focus');
13461             this.mimicing = true;
13462             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13463             if(this.monitorTab){
13464                 this.el.on("keydown", this.checkTab, this);
13465             }
13466         }
13467         */
13468     },
13469
13470     // private
13471     checkTab : function(e){
13472         if(e.getKey() == e.TAB){
13473             this.triggerBlur();
13474         }
13475     },
13476
13477     // private
13478     onBlur : function(){
13479         // do nothing
13480     },
13481
13482     // private
13483     mimicBlur : function(e, t){
13484         /*
13485         if(!this.wrap.contains(t) && this.validateBlur()){
13486             this.triggerBlur();
13487         }
13488         */
13489     },
13490
13491     // private
13492     triggerBlur : function(){
13493         this.mimicing = false;
13494         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13495         if(this.monitorTab){
13496             this.el.un("keydown", this.checkTab, this);
13497         }
13498         //this.wrap.removeClass('x-trigger-wrap-focus');
13499         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13500     },
13501
13502     // private
13503     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13504     validateBlur : function(e, t){
13505         return true;
13506     },
13507
13508     // private
13509     onDisable : function(){
13510         this.inputEl().dom.disabled = true;
13511         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13512         //if(this.wrap){
13513         //    this.wrap.addClass('x-item-disabled');
13514         //}
13515     },
13516
13517     // private
13518     onEnable : function(){
13519         this.inputEl().dom.disabled = false;
13520         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13521         //if(this.wrap){
13522         //    this.el.removeClass('x-item-disabled');
13523         //}
13524     },
13525
13526     // private
13527     onShow : function(){
13528         var ae = this.getActionEl();
13529         
13530         if(ae){
13531             ae.dom.style.display = '';
13532             ae.dom.style.visibility = 'visible';
13533         }
13534     },
13535
13536     // private
13537     
13538     onHide : function(){
13539         var ae = this.getActionEl();
13540         ae.dom.style.display = 'none';
13541     },
13542
13543     /**
13544      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13545      * by an implementing function.
13546      * @method
13547      * @param {EventObject} e
13548      */
13549     onTriggerClick : Roo.emptyFn
13550 });
13551  
13552 /*
13553 * Licence: LGPL
13554 */
13555
13556 /**
13557  * @class Roo.bootstrap.CardUploader
13558  * @extends Roo.bootstrap.Button
13559  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13560  * @cfg {Number} errorTimeout default 3000
13561  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13562  * @cfg {Array}  html The button text.
13563
13564  *
13565  * @constructor
13566  * Create a new CardUploader
13567  * @param {Object} config The config object
13568  */
13569
13570 Roo.bootstrap.CardUploader = function(config){
13571     
13572  
13573     
13574     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13575     
13576     
13577     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13578         return r.data.id
13579      });
13580     
13581      this.addEvents({
13582          // raw events
13583         /**
13584          * @event preview
13585          * When a image is clicked on - and needs to display a slideshow or similar..
13586          * @param {Roo.bootstrap.Card} this
13587          * @param {Object} The image information data 
13588          *
13589          */
13590         'preview' : true,
13591          /**
13592          * @event download
13593          * When a the download link is clicked
13594          * @param {Roo.bootstrap.Card} this
13595          * @param {Object} The image information data  contains 
13596          */
13597         'download' : true
13598         
13599     });
13600 };
13601  
13602 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13603     
13604      
13605     errorTimeout : 3000,
13606      
13607     images : false,
13608    
13609     fileCollection : false,
13610     allowBlank : true,
13611     
13612     getAutoCreate : function()
13613     {
13614         
13615         var cfg =  {
13616             cls :'form-group' ,
13617             cn : [
13618                
13619                 {
13620                     tag: 'label',
13621                    //cls : 'input-group-addon',
13622                     html : this.fieldLabel
13623
13624                 },
13625
13626                 {
13627                     tag: 'input',
13628                     type : 'hidden',
13629                     name : this.name,
13630                     value : this.value,
13631                     cls : 'd-none  form-control'
13632                 },
13633                 
13634                 {
13635                     tag: 'input',
13636                     multiple : 'multiple',
13637                     type : 'file',
13638                     cls : 'd-none  roo-card-upload-selector'
13639                 },
13640                 
13641                 {
13642                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13643                 },
13644                 {
13645                     cls : 'card-columns roo-card-uploader-container'
13646                 }
13647
13648             ]
13649         };
13650            
13651          
13652         return cfg;
13653     },
13654     
13655     getChildContainer : function() /// what children are added to.
13656     {
13657         return this.containerEl;
13658     },
13659    
13660     getButtonContainer : function() /// what children are added to.
13661     {
13662         return this.el.select(".roo-card-uploader-button-container").first();
13663     },
13664    
13665     initEvents : function()
13666     {
13667         
13668         Roo.bootstrap.Input.prototype.initEvents.call(this);
13669         
13670         var t = this;
13671         this.addxtype({
13672             xns: Roo.bootstrap,
13673
13674             xtype : 'Button',
13675             container_method : 'getButtonContainer' ,            
13676             html :  this.html, // fix changable?
13677             cls : 'w-100 ',
13678             listeners : {
13679                 'click' : function(btn, e) {
13680                     t.onClick(e);
13681                 }
13682             }
13683         });
13684         
13685         
13686         
13687         
13688         this.urlAPI = (window.createObjectURL && window) || 
13689                                 (window.URL && URL.revokeObjectURL && URL) || 
13690                                 (window.webkitURL && webkitURL);
13691                         
13692          
13693          
13694          
13695         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13696         
13697         this.selectorEl.on('change', this.onFileSelected, this);
13698         if (this.images) {
13699             var t = this;
13700             this.images.forEach(function(img) {
13701                 t.addCard(img)
13702             });
13703             this.images = false;
13704         }
13705         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13706          
13707        
13708     },
13709     
13710    
13711     onClick : function(e)
13712     {
13713         e.preventDefault();
13714          
13715         this.selectorEl.dom.click();
13716          
13717     },
13718     
13719     onFileSelected : function(e)
13720     {
13721         e.preventDefault();
13722         
13723         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13724             return;
13725         }
13726         
13727         Roo.each(this.selectorEl.dom.files, function(file){    
13728             this.addFile(file);
13729         }, this);
13730          
13731     },
13732     
13733       
13734     
13735       
13736     
13737     addFile : function(file)
13738     {
13739            
13740         if(typeof(file) === 'string'){
13741             throw "Add file by name?"; // should not happen
13742             return;
13743         }
13744         
13745         if(!file || !this.urlAPI){
13746             return;
13747         }
13748         
13749         // file;
13750         // file.type;
13751         
13752         var _this = this;
13753         
13754         
13755         var url = _this.urlAPI.createObjectURL( file);
13756            
13757         this.addCard({
13758             id : Roo.bootstrap.CardUploader.ID--,
13759             is_uploaded : false,
13760             src : url,
13761             srcfile : file,
13762             title : file.name,
13763             mimetype : file.type,
13764             preview : false,
13765             is_deleted : 0
13766         });
13767         
13768     },
13769     
13770     /**
13771      * addCard - add an Attachment to the uploader
13772      * @param data - the data about the image to upload
13773      *
13774      * {
13775           id : 123
13776           title : "Title of file",
13777           is_uploaded : false,
13778           src : "http://.....",
13779           srcfile : { the File upload object },
13780           mimetype : file.type,
13781           preview : false,
13782           is_deleted : 0
13783           .. any other data...
13784         }
13785      *
13786      * 
13787     */
13788     
13789     addCard : function (data)
13790     {
13791         // hidden input element?
13792         // if the file is not an image...
13793         //then we need to use something other that and header_image
13794         var t = this;
13795         //   remove.....
13796         var footer = [
13797             {
13798                 xns : Roo.bootstrap,
13799                 xtype : 'CardFooter',
13800                  items: [
13801                     {
13802                         xns : Roo.bootstrap,
13803                         xtype : 'Element',
13804                         cls : 'd-flex',
13805                         items : [
13806                             
13807                             {
13808                                 xns : Roo.bootstrap,
13809                                 xtype : 'Button',
13810                                 html : String.format("<small>{0}</small>", data.title),
13811                                 cls : 'col-10 text-left',
13812                                 size: 'sm',
13813                                 weight: 'link',
13814                                 fa : 'download',
13815                                 listeners : {
13816                                     click : function() {
13817                                      
13818                                         t.fireEvent( "download", t, data );
13819                                     }
13820                                 }
13821                             },
13822                           
13823                             {
13824                                 xns : Roo.bootstrap,
13825                                 xtype : 'Button',
13826                                 style: 'max-height: 28px; ',
13827                                 size : 'sm',
13828                                 weight: 'danger',
13829                                 cls : 'col-2',
13830                                 fa : 'times',
13831                                 listeners : {
13832                                     click : function() {
13833                                         t.removeCard(data.id)
13834                                     }
13835                                 }
13836                             }
13837                         ]
13838                     }
13839                     
13840                 ] 
13841             }
13842             
13843         ];
13844         
13845         var cn = this.addxtype(
13846             {
13847                  
13848                 xns : Roo.bootstrap,
13849                 xtype : 'Card',
13850                 closeable : true,
13851                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13852                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13853                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13854                 data : data,
13855                 html : false,
13856                  
13857                 items : footer,
13858                 initEvents : function() {
13859                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13860                     var card = this;
13861                     this.imgEl = this.el.select('.card-img-top').first();
13862                     if (this.imgEl) {
13863                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13864                         this.imgEl.set({ 'pointer' : 'cursor' });
13865                                   
13866                     }
13867                     this.getCardFooter().addClass('p-1');
13868                     
13869                   
13870                 }
13871                 
13872             }
13873         );
13874         // dont' really need ot update items.
13875         // this.items.push(cn);
13876         this.fileCollection.add(cn);
13877         
13878         if (!data.srcfile) {
13879             this.updateInput();
13880             return;
13881         }
13882             
13883         var _t = this;
13884         var reader = new FileReader();
13885         reader.addEventListener("load", function() {  
13886             data.srcdata =  reader.result;
13887             _t.updateInput();
13888         });
13889         reader.readAsDataURL(data.srcfile);
13890         
13891         
13892         
13893     },
13894     removeCard : function(id)
13895     {
13896         
13897         var card  = this.fileCollection.get(id);
13898         card.data.is_deleted = 1;
13899         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13900         //this.fileCollection.remove(card);
13901         //this.items = this.items.filter(function(e) { return e != card });
13902         // dont' really need ot update items.
13903         card.el.dom.parentNode.removeChild(card.el.dom);
13904         this.updateInput();
13905
13906         
13907     },
13908     reset: function()
13909     {
13910         this.fileCollection.each(function(card) {
13911             if (card.el.dom && card.el.dom.parentNode) {
13912                 card.el.dom.parentNode.removeChild(card.el.dom);
13913             }
13914         });
13915         this.fileCollection.clear();
13916         this.updateInput();
13917     },
13918     
13919     updateInput : function()
13920     {
13921          var data = [];
13922         this.fileCollection.each(function(e) {
13923             data.push(e.data);
13924             
13925         });
13926         this.inputEl().dom.value = JSON.stringify(data);
13927         
13928         
13929         
13930     }
13931     
13932     
13933 });
13934
13935
13936 Roo.bootstrap.CardUploader.ID = -1;/*
13937  * Based on:
13938  * Ext JS Library 1.1.1
13939  * Copyright(c) 2006-2007, Ext JS, LLC.
13940  *
13941  * Originally Released Under LGPL - original licence link has changed is not relivant.
13942  *
13943  * Fork - LGPL
13944  * <script type="text/javascript">
13945  */
13946
13947
13948 /**
13949  * @class Roo.data.SortTypes
13950  * @singleton
13951  * Defines the default sorting (casting?) comparison functions used when sorting data.
13952  */
13953 Roo.data.SortTypes = {
13954     /**
13955      * Default sort that does nothing
13956      * @param {Mixed} s The value being converted
13957      * @return {Mixed} The comparison value
13958      */
13959     none : function(s){
13960         return s;
13961     },
13962     
13963     /**
13964      * The regular expression used to strip tags
13965      * @type {RegExp}
13966      * @property
13967      */
13968     stripTagsRE : /<\/?[^>]+>/gi,
13969     
13970     /**
13971      * Strips all HTML tags to sort on text only
13972      * @param {Mixed} s The value being converted
13973      * @return {String} The comparison value
13974      */
13975     asText : function(s){
13976         return String(s).replace(this.stripTagsRE, "");
13977     },
13978     
13979     /**
13980      * Strips all HTML tags to sort on text only - Case insensitive
13981      * @param {Mixed} s The value being converted
13982      * @return {String} The comparison value
13983      */
13984     asUCText : function(s){
13985         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13986     },
13987     
13988     /**
13989      * Case insensitive string
13990      * @param {Mixed} s The value being converted
13991      * @return {String} The comparison value
13992      */
13993     asUCString : function(s) {
13994         return String(s).toUpperCase();
13995     },
13996     
13997     /**
13998      * Date sorting
13999      * @param {Mixed} s The value being converted
14000      * @return {Number} The comparison value
14001      */
14002     asDate : function(s) {
14003         if(!s){
14004             return 0;
14005         }
14006         if(s instanceof Date){
14007             return s.getTime();
14008         }
14009         return Date.parse(String(s));
14010     },
14011     
14012     /**
14013      * Float sorting
14014      * @param {Mixed} s The value being converted
14015      * @return {Float} The comparison value
14016      */
14017     asFloat : function(s) {
14018         var val = parseFloat(String(s).replace(/,/g, ""));
14019         if(isNaN(val)) {
14020             val = 0;
14021         }
14022         return val;
14023     },
14024     
14025     /**
14026      * Integer sorting
14027      * @param {Mixed} s The value being converted
14028      * @return {Number} The comparison value
14029      */
14030     asInt : function(s) {
14031         var val = parseInt(String(s).replace(/,/g, ""));
14032         if(isNaN(val)) {
14033             val = 0;
14034         }
14035         return val;
14036     }
14037 };/*
14038  * Based on:
14039  * Ext JS Library 1.1.1
14040  * Copyright(c) 2006-2007, Ext JS, LLC.
14041  *
14042  * Originally Released Under LGPL - original licence link has changed is not relivant.
14043  *
14044  * Fork - LGPL
14045  * <script type="text/javascript">
14046  */
14047
14048 /**
14049 * @class Roo.data.Record
14050  * Instances of this class encapsulate both record <em>definition</em> information, and record
14051  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14052  * to access Records cached in an {@link Roo.data.Store} object.<br>
14053  * <p>
14054  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14055  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14056  * objects.<br>
14057  * <p>
14058  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14059  * @constructor
14060  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14061  * {@link #create}. The parameters are the same.
14062  * @param {Array} data An associative Array of data values keyed by the field name.
14063  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14064  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14065  * not specified an integer id is generated.
14066  */
14067 Roo.data.Record = function(data, id){
14068     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14069     this.data = data;
14070 };
14071
14072 /**
14073  * Generate a constructor for a specific record layout.
14074  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14075  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14076  * Each field definition object may contain the following properties: <ul>
14077  * <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,
14078  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14079  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14080  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14081  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14082  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14083  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14084  * this may be omitted.</p></li>
14085  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14086  * <ul><li>auto (Default, implies no conversion)</li>
14087  * <li>string</li>
14088  * <li>int</li>
14089  * <li>float</li>
14090  * <li>boolean</li>
14091  * <li>date</li></ul></p></li>
14092  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14093  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14094  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14095  * by the Reader into an object that will be stored in the Record. It is passed the
14096  * following parameters:<ul>
14097  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14098  * </ul></p></li>
14099  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14100  * </ul>
14101  * <br>usage:<br><pre><code>
14102 var TopicRecord = Roo.data.Record.create(
14103     {name: 'title', mapping: 'topic_title'},
14104     {name: 'author', mapping: 'username'},
14105     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14106     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14107     {name: 'lastPoster', mapping: 'user2'},
14108     {name: 'excerpt', mapping: 'post_text'}
14109 );
14110
14111 var myNewRecord = new TopicRecord({
14112     title: 'Do my job please',
14113     author: 'noobie',
14114     totalPosts: 1,
14115     lastPost: new Date(),
14116     lastPoster: 'Animal',
14117     excerpt: 'No way dude!'
14118 });
14119 myStore.add(myNewRecord);
14120 </code></pre>
14121  * @method create
14122  * @static
14123  */
14124 Roo.data.Record.create = function(o){
14125     var f = function(){
14126         f.superclass.constructor.apply(this, arguments);
14127     };
14128     Roo.extend(f, Roo.data.Record);
14129     var p = f.prototype;
14130     p.fields = new Roo.util.MixedCollection(false, function(field){
14131         return field.name;
14132     });
14133     for(var i = 0, len = o.length; i < len; i++){
14134         p.fields.add(new Roo.data.Field(o[i]));
14135     }
14136     f.getField = function(name){
14137         return p.fields.get(name);  
14138     };
14139     return f;
14140 };
14141
14142 Roo.data.Record.AUTO_ID = 1000;
14143 Roo.data.Record.EDIT = 'edit';
14144 Roo.data.Record.REJECT = 'reject';
14145 Roo.data.Record.COMMIT = 'commit';
14146
14147 Roo.data.Record.prototype = {
14148     /**
14149      * Readonly flag - true if this record has been modified.
14150      * @type Boolean
14151      */
14152     dirty : false,
14153     editing : false,
14154     error: null,
14155     modified: null,
14156
14157     // private
14158     join : function(store){
14159         this.store = store;
14160     },
14161
14162     /**
14163      * Set the named field to the specified value.
14164      * @param {String} name The name of the field to set.
14165      * @param {Object} value The value to set the field to.
14166      */
14167     set : function(name, value){
14168         if(this.data[name] == value){
14169             return;
14170         }
14171         this.dirty = true;
14172         if(!this.modified){
14173             this.modified = {};
14174         }
14175         if(typeof this.modified[name] == 'undefined'){
14176             this.modified[name] = this.data[name];
14177         }
14178         this.data[name] = value;
14179         if(!this.editing && this.store){
14180             this.store.afterEdit(this);
14181         }       
14182     },
14183
14184     /**
14185      * Get the value of the named field.
14186      * @param {String} name The name of the field to get the value of.
14187      * @return {Object} The value of the field.
14188      */
14189     get : function(name){
14190         return this.data[name]; 
14191     },
14192
14193     // private
14194     beginEdit : function(){
14195         this.editing = true;
14196         this.modified = {}; 
14197     },
14198
14199     // private
14200     cancelEdit : function(){
14201         this.editing = false;
14202         delete this.modified;
14203     },
14204
14205     // private
14206     endEdit : function(){
14207         this.editing = false;
14208         if(this.dirty && this.store){
14209             this.store.afterEdit(this);
14210         }
14211     },
14212
14213     /**
14214      * Usually called by the {@link Roo.data.Store} which owns the Record.
14215      * Rejects all changes made to the Record since either creation, or the last commit operation.
14216      * Modified fields are reverted to their original values.
14217      * <p>
14218      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14219      * of reject operations.
14220      */
14221     reject : function(){
14222         var m = this.modified;
14223         for(var n in m){
14224             if(typeof m[n] != "function"){
14225                 this.data[n] = m[n];
14226             }
14227         }
14228         this.dirty = false;
14229         delete this.modified;
14230         this.editing = false;
14231         if(this.store){
14232             this.store.afterReject(this);
14233         }
14234     },
14235
14236     /**
14237      * Usually called by the {@link Roo.data.Store} which owns the Record.
14238      * Commits all changes made to the Record since either creation, or the last commit operation.
14239      * <p>
14240      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14241      * of commit operations.
14242      */
14243     commit : function(){
14244         this.dirty = false;
14245         delete this.modified;
14246         this.editing = false;
14247         if(this.store){
14248             this.store.afterCommit(this);
14249         }
14250     },
14251
14252     // private
14253     hasError : function(){
14254         return this.error != null;
14255     },
14256
14257     // private
14258     clearError : function(){
14259         this.error = null;
14260     },
14261
14262     /**
14263      * Creates a copy of this record.
14264      * @param {String} id (optional) A new record id if you don't want to use this record's id
14265      * @return {Record}
14266      */
14267     copy : function(newId) {
14268         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14269     }
14270 };/*
14271  * Based on:
14272  * Ext JS Library 1.1.1
14273  * Copyright(c) 2006-2007, Ext JS, LLC.
14274  *
14275  * Originally Released Under LGPL - original licence link has changed is not relivant.
14276  *
14277  * Fork - LGPL
14278  * <script type="text/javascript">
14279  */
14280
14281
14282
14283 /**
14284  * @class Roo.data.Store
14285  * @extends Roo.util.Observable
14286  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14287  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14288  * <p>
14289  * 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
14290  * has no knowledge of the format of the data returned by the Proxy.<br>
14291  * <p>
14292  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14293  * instances from the data object. These records are cached and made available through accessor functions.
14294  * @constructor
14295  * Creates a new Store.
14296  * @param {Object} config A config object containing the objects needed for the Store to access data,
14297  * and read the data into Records.
14298  */
14299 Roo.data.Store = function(config){
14300     this.data = new Roo.util.MixedCollection(false);
14301     this.data.getKey = function(o){
14302         return o.id;
14303     };
14304     this.baseParams = {};
14305     // private
14306     this.paramNames = {
14307         "start" : "start",
14308         "limit" : "limit",
14309         "sort" : "sort",
14310         "dir" : "dir",
14311         "multisort" : "_multisort"
14312     };
14313
14314     if(config && config.data){
14315         this.inlineData = config.data;
14316         delete config.data;
14317     }
14318
14319     Roo.apply(this, config);
14320     
14321     if(this.reader){ // reader passed
14322         this.reader = Roo.factory(this.reader, Roo.data);
14323         this.reader.xmodule = this.xmodule || false;
14324         if(!this.recordType){
14325             this.recordType = this.reader.recordType;
14326         }
14327         if(this.reader.onMetaChange){
14328             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14329         }
14330     }
14331
14332     if(this.recordType){
14333         this.fields = this.recordType.prototype.fields;
14334     }
14335     this.modified = [];
14336
14337     this.addEvents({
14338         /**
14339          * @event datachanged
14340          * Fires when the data cache has changed, and a widget which is using this Store
14341          * as a Record cache should refresh its view.
14342          * @param {Store} this
14343          */
14344         datachanged : true,
14345         /**
14346          * @event metachange
14347          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14348          * @param {Store} this
14349          * @param {Object} meta The JSON metadata
14350          */
14351         metachange : true,
14352         /**
14353          * @event add
14354          * Fires when Records have been added to the Store
14355          * @param {Store} this
14356          * @param {Roo.data.Record[]} records The array of Records added
14357          * @param {Number} index The index at which the record(s) were added
14358          */
14359         add : true,
14360         /**
14361          * @event remove
14362          * Fires when a Record has been removed from the Store
14363          * @param {Store} this
14364          * @param {Roo.data.Record} record The Record that was removed
14365          * @param {Number} index The index at which the record was removed
14366          */
14367         remove : true,
14368         /**
14369          * @event update
14370          * Fires when a Record has been updated
14371          * @param {Store} this
14372          * @param {Roo.data.Record} record The Record that was updated
14373          * @param {String} operation The update operation being performed.  Value may be one of:
14374          * <pre><code>
14375  Roo.data.Record.EDIT
14376  Roo.data.Record.REJECT
14377  Roo.data.Record.COMMIT
14378          * </code></pre>
14379          */
14380         update : true,
14381         /**
14382          * @event clear
14383          * Fires when the data cache has been cleared.
14384          * @param {Store} this
14385          */
14386         clear : true,
14387         /**
14388          * @event beforeload
14389          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14390          * the load action will be canceled.
14391          * @param {Store} this
14392          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14393          */
14394         beforeload : true,
14395         /**
14396          * @event beforeloadadd
14397          * Fires after a new set of Records has been loaded.
14398          * @param {Store} this
14399          * @param {Roo.data.Record[]} records The Records that were loaded
14400          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14401          */
14402         beforeloadadd : true,
14403         /**
14404          * @event load
14405          * Fires after a new set of Records has been loaded, before they are added to the store.
14406          * @param {Store} this
14407          * @param {Roo.data.Record[]} records The Records that were loaded
14408          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14409          * @params {Object} return from reader
14410          */
14411         load : true,
14412         /**
14413          * @event loadexception
14414          * Fires if an exception occurs in the Proxy during loading.
14415          * Called with the signature of the Proxy's "loadexception" event.
14416          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14417          * 
14418          * @param {Proxy} 
14419          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14420          * @param {Object} load options 
14421          * @param {Object} jsonData from your request (normally this contains the Exception)
14422          */
14423         loadexception : true
14424     });
14425     
14426     if(this.proxy){
14427         this.proxy = Roo.factory(this.proxy, Roo.data);
14428         this.proxy.xmodule = this.xmodule || false;
14429         this.relayEvents(this.proxy,  ["loadexception"]);
14430     }
14431     this.sortToggle = {};
14432     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14433
14434     Roo.data.Store.superclass.constructor.call(this);
14435
14436     if(this.inlineData){
14437         this.loadData(this.inlineData);
14438         delete this.inlineData;
14439     }
14440 };
14441
14442 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14443      /**
14444     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14445     * without a remote query - used by combo/forms at present.
14446     */
14447     
14448     /**
14449     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14450     */
14451     /**
14452     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14453     */
14454     /**
14455     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14456     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14457     */
14458     /**
14459     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14460     * on any HTTP request
14461     */
14462     /**
14463     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14464     */
14465     /**
14466     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14467     */
14468     multiSort: false,
14469     /**
14470     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14471     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14472     */
14473     remoteSort : false,
14474
14475     /**
14476     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14477      * loaded or when a record is removed. (defaults to false).
14478     */
14479     pruneModifiedRecords : false,
14480
14481     // private
14482     lastOptions : null,
14483
14484     /**
14485      * Add Records to the Store and fires the add event.
14486      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14487      */
14488     add : function(records){
14489         records = [].concat(records);
14490         for(var i = 0, len = records.length; i < len; i++){
14491             records[i].join(this);
14492         }
14493         var index = this.data.length;
14494         this.data.addAll(records);
14495         this.fireEvent("add", this, records, index);
14496     },
14497
14498     /**
14499      * Remove a Record from the Store and fires the remove event.
14500      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14501      */
14502     remove : function(record){
14503         var index = this.data.indexOf(record);
14504         this.data.removeAt(index);
14505  
14506         if(this.pruneModifiedRecords){
14507             this.modified.remove(record);
14508         }
14509         this.fireEvent("remove", this, record, index);
14510     },
14511
14512     /**
14513      * Remove all Records from the Store and fires the clear event.
14514      */
14515     removeAll : function(){
14516         this.data.clear();
14517         if(this.pruneModifiedRecords){
14518             this.modified = [];
14519         }
14520         this.fireEvent("clear", this);
14521     },
14522
14523     /**
14524      * Inserts Records to the Store at the given index and fires the add event.
14525      * @param {Number} index The start index at which to insert the passed Records.
14526      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14527      */
14528     insert : function(index, records){
14529         records = [].concat(records);
14530         for(var i = 0, len = records.length; i < len; i++){
14531             this.data.insert(index, records[i]);
14532             records[i].join(this);
14533         }
14534         this.fireEvent("add", this, records, index);
14535     },
14536
14537     /**
14538      * Get the index within the cache of the passed Record.
14539      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14540      * @return {Number} The index of the passed Record. Returns -1 if not found.
14541      */
14542     indexOf : function(record){
14543         return this.data.indexOf(record);
14544     },
14545
14546     /**
14547      * Get the index within the cache of the Record with the passed id.
14548      * @param {String} id The id of the Record to find.
14549      * @return {Number} The index of the Record. Returns -1 if not found.
14550      */
14551     indexOfId : function(id){
14552         return this.data.indexOfKey(id);
14553     },
14554
14555     /**
14556      * Get the Record with the specified id.
14557      * @param {String} id The id of the Record to find.
14558      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14559      */
14560     getById : function(id){
14561         return this.data.key(id);
14562     },
14563
14564     /**
14565      * Get the Record at the specified index.
14566      * @param {Number} index The index of the Record to find.
14567      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14568      */
14569     getAt : function(index){
14570         return this.data.itemAt(index);
14571     },
14572
14573     /**
14574      * Returns a range of Records between specified indices.
14575      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14576      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14577      * @return {Roo.data.Record[]} An array of Records
14578      */
14579     getRange : function(start, end){
14580         return this.data.getRange(start, end);
14581     },
14582
14583     // private
14584     storeOptions : function(o){
14585         o = Roo.apply({}, o);
14586         delete o.callback;
14587         delete o.scope;
14588         this.lastOptions = o;
14589     },
14590
14591     /**
14592      * Loads the Record cache from the configured Proxy using the configured Reader.
14593      * <p>
14594      * If using remote paging, then the first load call must specify the <em>start</em>
14595      * and <em>limit</em> properties in the options.params property to establish the initial
14596      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14597      * <p>
14598      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14599      * and this call will return before the new data has been loaded. Perform any post-processing
14600      * in a callback function, or in a "load" event handler.</strong>
14601      * <p>
14602      * @param {Object} options An object containing properties which control loading options:<ul>
14603      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14604      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14605      * passed the following arguments:<ul>
14606      * <li>r : Roo.data.Record[]</li>
14607      * <li>options: Options object from the load call</li>
14608      * <li>success: Boolean success indicator</li></ul></li>
14609      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14610      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14611      * </ul>
14612      */
14613     load : function(options){
14614         options = options || {};
14615         if(this.fireEvent("beforeload", this, options) !== false){
14616             this.storeOptions(options);
14617             var p = Roo.apply(options.params || {}, this.baseParams);
14618             // if meta was not loaded from remote source.. try requesting it.
14619             if (!this.reader.metaFromRemote) {
14620                 p._requestMeta = 1;
14621             }
14622             if(this.sortInfo && this.remoteSort){
14623                 var pn = this.paramNames;
14624                 p[pn["sort"]] = this.sortInfo.field;
14625                 p[pn["dir"]] = this.sortInfo.direction;
14626             }
14627             if (this.multiSort) {
14628                 var pn = this.paramNames;
14629                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14630             }
14631             
14632             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14633         }
14634     },
14635
14636     /**
14637      * Reloads the Record cache from the configured Proxy using the configured Reader and
14638      * the options from the last load operation performed.
14639      * @param {Object} options (optional) An object containing properties which may override the options
14640      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14641      * the most recently used options are reused).
14642      */
14643     reload : function(options){
14644         this.load(Roo.applyIf(options||{}, this.lastOptions));
14645     },
14646
14647     // private
14648     // Called as a callback by the Reader during a load operation.
14649     loadRecords : function(o, options, success){
14650         if(!o || success === false){
14651             if(success !== false){
14652                 this.fireEvent("load", this, [], options, o);
14653             }
14654             if(options.callback){
14655                 options.callback.call(options.scope || this, [], options, false);
14656             }
14657             return;
14658         }
14659         // if data returned failure - throw an exception.
14660         if (o.success === false) {
14661             // show a message if no listener is registered.
14662             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14663                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14664             }
14665             // loadmask wil be hooked into this..
14666             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14667             return;
14668         }
14669         var r = o.records, t = o.totalRecords || r.length;
14670         
14671         this.fireEvent("beforeloadadd", this, r, options, o);
14672         
14673         if(!options || options.add !== true){
14674             if(this.pruneModifiedRecords){
14675                 this.modified = [];
14676             }
14677             for(var i = 0, len = r.length; i < len; i++){
14678                 r[i].join(this);
14679             }
14680             if(this.snapshot){
14681                 this.data = this.snapshot;
14682                 delete this.snapshot;
14683             }
14684             this.data.clear();
14685             this.data.addAll(r);
14686             this.totalLength = t;
14687             this.applySort();
14688             this.fireEvent("datachanged", this);
14689         }else{
14690             this.totalLength = Math.max(t, this.data.length+r.length);
14691             this.add(r);
14692         }
14693         
14694         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14695                 
14696             var e = new Roo.data.Record({});
14697
14698             e.set(this.parent.displayField, this.parent.emptyTitle);
14699             e.set(this.parent.valueField, '');
14700
14701             this.insert(0, e);
14702         }
14703             
14704         this.fireEvent("load", this, r, options, o);
14705         if(options.callback){
14706             options.callback.call(options.scope || this, r, options, true);
14707         }
14708     },
14709
14710
14711     /**
14712      * Loads data from a passed data block. A Reader which understands the format of the data
14713      * must have been configured in the constructor.
14714      * @param {Object} data The data block from which to read the Records.  The format of the data expected
14715      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14716      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14717      */
14718     loadData : function(o, append){
14719         var r = this.reader.readRecords(o);
14720         this.loadRecords(r, {add: append}, true);
14721     },
14722     
14723      /**
14724      * using 'cn' the nested child reader read the child array into it's child stores.
14725      * @param {Object} rec The record with a 'children array
14726      */
14727     loadDataFromChildren : function(rec)
14728     {
14729         this.loadData(this.reader.toLoadData(rec));
14730     },
14731     
14732
14733     /**
14734      * Gets the number of cached records.
14735      * <p>
14736      * <em>If using paging, this may not be the total size of the dataset. If the data object
14737      * used by the Reader contains the dataset size, then the getTotalCount() function returns
14738      * the data set size</em>
14739      */
14740     getCount : function(){
14741         return this.data.length || 0;
14742     },
14743
14744     /**
14745      * Gets the total number of records in the dataset as returned by the server.
14746      * <p>
14747      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14748      * the dataset size</em>
14749      */
14750     getTotalCount : function(){
14751         return this.totalLength || 0;
14752     },
14753
14754     /**
14755      * Returns the sort state of the Store as an object with two properties:
14756      * <pre><code>
14757  field {String} The name of the field by which the Records are sorted
14758  direction {String} The sort order, "ASC" or "DESC"
14759      * </code></pre>
14760      */
14761     getSortState : function(){
14762         return this.sortInfo;
14763     },
14764
14765     // private
14766     applySort : function(){
14767         if(this.sortInfo && !this.remoteSort){
14768             var s = this.sortInfo, f = s.field;
14769             var st = this.fields.get(f).sortType;
14770             var fn = function(r1, r2){
14771                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14772                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14773             };
14774             this.data.sort(s.direction, fn);
14775             if(this.snapshot && this.snapshot != this.data){
14776                 this.snapshot.sort(s.direction, fn);
14777             }
14778         }
14779     },
14780
14781     /**
14782      * Sets the default sort column and order to be used by the next load operation.
14783      * @param {String} fieldName The name of the field to sort by.
14784      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14785      */
14786     setDefaultSort : function(field, dir){
14787         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14788     },
14789
14790     /**
14791      * Sort the Records.
14792      * If remote sorting is used, the sort is performed on the server, and the cache is
14793      * reloaded. If local sorting is used, the cache is sorted internally.
14794      * @param {String} fieldName The name of the field to sort by.
14795      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14796      */
14797     sort : function(fieldName, dir){
14798         var f = this.fields.get(fieldName);
14799         if(!dir){
14800             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14801             
14802             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14803                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14804             }else{
14805                 dir = f.sortDir;
14806             }
14807         }
14808         this.sortToggle[f.name] = dir;
14809         this.sortInfo = {field: f.name, direction: dir};
14810         if(!this.remoteSort){
14811             this.applySort();
14812             this.fireEvent("datachanged", this);
14813         }else{
14814             this.load(this.lastOptions);
14815         }
14816     },
14817
14818     /**
14819      * Calls the specified function for each of the Records in the cache.
14820      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14821      * Returning <em>false</em> aborts and exits the iteration.
14822      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14823      */
14824     each : function(fn, scope){
14825         this.data.each(fn, scope);
14826     },
14827
14828     /**
14829      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14830      * (e.g., during paging).
14831      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14832      */
14833     getModifiedRecords : function(){
14834         return this.modified;
14835     },
14836
14837     // private
14838     createFilterFn : function(property, value, anyMatch){
14839         if(!value.exec){ // not a regex
14840             value = String(value);
14841             if(value.length == 0){
14842                 return false;
14843             }
14844             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14845         }
14846         return function(r){
14847             return value.test(r.data[property]);
14848         };
14849     },
14850
14851     /**
14852      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14853      * @param {String} property A field on your records
14854      * @param {Number} start The record index to start at (defaults to 0)
14855      * @param {Number} end The last record index to include (defaults to length - 1)
14856      * @return {Number} The sum
14857      */
14858     sum : function(property, start, end){
14859         var rs = this.data.items, v = 0;
14860         start = start || 0;
14861         end = (end || end === 0) ? end : rs.length-1;
14862
14863         for(var i = start; i <= end; i++){
14864             v += (rs[i].data[property] || 0);
14865         }
14866         return v;
14867     },
14868
14869     /**
14870      * Filter the records by a specified property.
14871      * @param {String} field A field on your records
14872      * @param {String/RegExp} value Either a string that the field
14873      * should start with or a RegExp to test against the field
14874      * @param {Boolean} anyMatch True to match any part not just the beginning
14875      */
14876     filter : function(property, value, anyMatch){
14877         var fn = this.createFilterFn(property, value, anyMatch);
14878         return fn ? this.filterBy(fn) : this.clearFilter();
14879     },
14880
14881     /**
14882      * Filter by a function. The specified function will be called with each
14883      * record in this data source. If the function returns true the record is included,
14884      * otherwise it is filtered.
14885      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14886      * @param {Object} scope (optional) The scope of the function (defaults to this)
14887      */
14888     filterBy : function(fn, scope){
14889         this.snapshot = this.snapshot || this.data;
14890         this.data = this.queryBy(fn, scope||this);
14891         this.fireEvent("datachanged", this);
14892     },
14893
14894     /**
14895      * Query the records by a specified property.
14896      * @param {String} field A field on your records
14897      * @param {String/RegExp} value Either a string that the field
14898      * should start with or a RegExp to test against the field
14899      * @param {Boolean} anyMatch True to match any part not just the beginning
14900      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14901      */
14902     query : function(property, value, anyMatch){
14903         var fn = this.createFilterFn(property, value, anyMatch);
14904         return fn ? this.queryBy(fn) : this.data.clone();
14905     },
14906
14907     /**
14908      * Query by a function. The specified function will be called with each
14909      * record in this data source. If the function returns true the record is included
14910      * in the results.
14911      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14912      * @param {Object} scope (optional) The scope of the function (defaults to this)
14913       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14914      **/
14915     queryBy : function(fn, scope){
14916         var data = this.snapshot || this.data;
14917         return data.filterBy(fn, scope||this);
14918     },
14919
14920     /**
14921      * Collects unique values for a particular dataIndex from this store.
14922      * @param {String} dataIndex The property to collect
14923      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14924      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14925      * @return {Array} An array of the unique values
14926      **/
14927     collect : function(dataIndex, allowNull, bypassFilter){
14928         var d = (bypassFilter === true && this.snapshot) ?
14929                 this.snapshot.items : this.data.items;
14930         var v, sv, r = [], l = {};
14931         for(var i = 0, len = d.length; i < len; i++){
14932             v = d[i].data[dataIndex];
14933             sv = String(v);
14934             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14935                 l[sv] = true;
14936                 r[r.length] = v;
14937             }
14938         }
14939         return r;
14940     },
14941
14942     /**
14943      * Revert to a view of the Record cache with no filtering applied.
14944      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14945      */
14946     clearFilter : function(suppressEvent){
14947         if(this.snapshot && this.snapshot != this.data){
14948             this.data = this.snapshot;
14949             delete this.snapshot;
14950             if(suppressEvent !== true){
14951                 this.fireEvent("datachanged", this);
14952             }
14953         }
14954     },
14955
14956     // private
14957     afterEdit : function(record){
14958         if(this.modified.indexOf(record) == -1){
14959             this.modified.push(record);
14960         }
14961         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14962     },
14963     
14964     // private
14965     afterReject : function(record){
14966         this.modified.remove(record);
14967         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14968     },
14969
14970     // private
14971     afterCommit : function(record){
14972         this.modified.remove(record);
14973         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14974     },
14975
14976     /**
14977      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14978      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14979      */
14980     commitChanges : function(){
14981         var m = this.modified.slice(0);
14982         this.modified = [];
14983         for(var i = 0, len = m.length; i < len; i++){
14984             m[i].commit();
14985         }
14986     },
14987
14988     /**
14989      * Cancel outstanding changes on all changed records.
14990      */
14991     rejectChanges : function(){
14992         var m = this.modified.slice(0);
14993         this.modified = [];
14994         for(var i = 0, len = m.length; i < len; i++){
14995             m[i].reject();
14996         }
14997     },
14998
14999     onMetaChange : function(meta, rtype, o){
15000         this.recordType = rtype;
15001         this.fields = rtype.prototype.fields;
15002         delete this.snapshot;
15003         this.sortInfo = meta.sortInfo || this.sortInfo;
15004         this.modified = [];
15005         this.fireEvent('metachange', this, this.reader.meta);
15006     },
15007     
15008     moveIndex : function(data, type)
15009     {
15010         var index = this.indexOf(data);
15011         
15012         var newIndex = index + type;
15013         
15014         this.remove(data);
15015         
15016         this.insert(newIndex, data);
15017         
15018     }
15019 });/*
15020  * Based on:
15021  * Ext JS Library 1.1.1
15022  * Copyright(c) 2006-2007, Ext JS, LLC.
15023  *
15024  * Originally Released Under LGPL - original licence link has changed is not relivant.
15025  *
15026  * Fork - LGPL
15027  * <script type="text/javascript">
15028  */
15029
15030 /**
15031  * @class Roo.data.SimpleStore
15032  * @extends Roo.data.Store
15033  * Small helper class to make creating Stores from Array data easier.
15034  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15035  * @cfg {Array} fields An array of field definition objects, or field name strings.
15036  * @cfg {Object} an existing reader (eg. copied from another store)
15037  * @cfg {Array} data The multi-dimensional array of data
15038  * @constructor
15039  * @param {Object} config
15040  */
15041 Roo.data.SimpleStore = function(config)
15042 {
15043     Roo.data.SimpleStore.superclass.constructor.call(this, {
15044         isLocal : true,
15045         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15046                 id: config.id
15047             },
15048             Roo.data.Record.create(config.fields)
15049         ),
15050         proxy : new Roo.data.MemoryProxy(config.data)
15051     });
15052     this.load();
15053 };
15054 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15055  * Based on:
15056  * Ext JS Library 1.1.1
15057  * Copyright(c) 2006-2007, Ext JS, LLC.
15058  *
15059  * Originally Released Under LGPL - original licence link has changed is not relivant.
15060  *
15061  * Fork - LGPL
15062  * <script type="text/javascript">
15063  */
15064
15065 /**
15066 /**
15067  * @extends Roo.data.Store
15068  * @class Roo.data.JsonStore
15069  * Small helper class to make creating Stores for JSON data easier. <br/>
15070 <pre><code>
15071 var store = new Roo.data.JsonStore({
15072     url: 'get-images.php',
15073     root: 'images',
15074     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15075 });
15076 </code></pre>
15077  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15078  * JsonReader and HttpProxy (unless inline data is provided).</b>
15079  * @cfg {Array} fields An array of field definition objects, or field name strings.
15080  * @constructor
15081  * @param {Object} config
15082  */
15083 Roo.data.JsonStore = function(c){
15084     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15085         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15086         reader: new Roo.data.JsonReader(c, c.fields)
15087     }));
15088 };
15089 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15090  * Based on:
15091  * Ext JS Library 1.1.1
15092  * Copyright(c) 2006-2007, Ext JS, LLC.
15093  *
15094  * Originally Released Under LGPL - original licence link has changed is not relivant.
15095  *
15096  * Fork - LGPL
15097  * <script type="text/javascript">
15098  */
15099
15100  
15101 Roo.data.Field = function(config){
15102     if(typeof config == "string"){
15103         config = {name: config};
15104     }
15105     Roo.apply(this, config);
15106     
15107     if(!this.type){
15108         this.type = "auto";
15109     }
15110     
15111     var st = Roo.data.SortTypes;
15112     // named sortTypes are supported, here we look them up
15113     if(typeof this.sortType == "string"){
15114         this.sortType = st[this.sortType];
15115     }
15116     
15117     // set default sortType for strings and dates
15118     if(!this.sortType){
15119         switch(this.type){
15120             case "string":
15121                 this.sortType = st.asUCString;
15122                 break;
15123             case "date":
15124                 this.sortType = st.asDate;
15125                 break;
15126             default:
15127                 this.sortType = st.none;
15128         }
15129     }
15130
15131     // define once
15132     var stripRe = /[\$,%]/g;
15133
15134     // prebuilt conversion function for this field, instead of
15135     // switching every time we're reading a value
15136     if(!this.convert){
15137         var cv, dateFormat = this.dateFormat;
15138         switch(this.type){
15139             case "":
15140             case "auto":
15141             case undefined:
15142                 cv = function(v){ return v; };
15143                 break;
15144             case "string":
15145                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15146                 break;
15147             case "int":
15148                 cv = function(v){
15149                     return v !== undefined && v !== null && v !== '' ?
15150                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15151                     };
15152                 break;
15153             case "float":
15154                 cv = function(v){
15155                     return v !== undefined && v !== null && v !== '' ?
15156                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15157                     };
15158                 break;
15159             case "bool":
15160             case "boolean":
15161                 cv = function(v){ return v === true || v === "true" || v == 1; };
15162                 break;
15163             case "date":
15164                 cv = function(v){
15165                     if(!v){
15166                         return '';
15167                     }
15168                     if(v instanceof Date){
15169                         return v;
15170                     }
15171                     if(dateFormat){
15172                         if(dateFormat == "timestamp"){
15173                             return new Date(v*1000);
15174                         }
15175                         return Date.parseDate(v, dateFormat);
15176                     }
15177                     var parsed = Date.parse(v);
15178                     return parsed ? new Date(parsed) : null;
15179                 };
15180              break;
15181             
15182         }
15183         this.convert = cv;
15184     }
15185 };
15186
15187 Roo.data.Field.prototype = {
15188     dateFormat: null,
15189     defaultValue: "",
15190     mapping: null,
15191     sortType : null,
15192     sortDir : "ASC"
15193 };/*
15194  * Based on:
15195  * Ext JS Library 1.1.1
15196  * Copyright(c) 2006-2007, Ext JS, LLC.
15197  *
15198  * Originally Released Under LGPL - original licence link has changed is not relivant.
15199  *
15200  * Fork - LGPL
15201  * <script type="text/javascript">
15202  */
15203  
15204 // Base class for reading structured data from a data source.  This class is intended to be
15205 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15206
15207 /**
15208  * @class Roo.data.DataReader
15209  * Base class for reading structured data from a data source.  This class is intended to be
15210  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15211  */
15212
15213 Roo.data.DataReader = function(meta, recordType){
15214     
15215     this.meta = meta;
15216     
15217     this.recordType = recordType instanceof Array ? 
15218         Roo.data.Record.create(recordType) : recordType;
15219 };
15220
15221 Roo.data.DataReader.prototype = {
15222     
15223     
15224     readerType : 'Data',
15225      /**
15226      * Create an empty record
15227      * @param {Object} data (optional) - overlay some values
15228      * @return {Roo.data.Record} record created.
15229      */
15230     newRow :  function(d) {
15231         var da =  {};
15232         this.recordType.prototype.fields.each(function(c) {
15233             switch( c.type) {
15234                 case 'int' : da[c.name] = 0; break;
15235                 case 'date' : da[c.name] = new Date(); break;
15236                 case 'float' : da[c.name] = 0.0; break;
15237                 case 'boolean' : da[c.name] = false; break;
15238                 default : da[c.name] = ""; break;
15239             }
15240             
15241         });
15242         return new this.recordType(Roo.apply(da, d));
15243     }
15244     
15245     
15246 };/*
15247  * Based on:
15248  * Ext JS Library 1.1.1
15249  * Copyright(c) 2006-2007, Ext JS, LLC.
15250  *
15251  * Originally Released Under LGPL - original licence link has changed is not relivant.
15252  *
15253  * Fork - LGPL
15254  * <script type="text/javascript">
15255  */
15256
15257 /**
15258  * @class Roo.data.DataProxy
15259  * @extends Roo.data.Observable
15260  * This class is an abstract base class for implementations which provide retrieval of
15261  * unformatted data objects.<br>
15262  * <p>
15263  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15264  * (of the appropriate type which knows how to parse the data object) to provide a block of
15265  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15266  * <p>
15267  * Custom implementations must implement the load method as described in
15268  * {@link Roo.data.HttpProxy#load}.
15269  */
15270 Roo.data.DataProxy = function(){
15271     this.addEvents({
15272         /**
15273          * @event beforeload
15274          * Fires before a network request is made to retrieve a data object.
15275          * @param {Object} This DataProxy object.
15276          * @param {Object} params The params parameter to the load function.
15277          */
15278         beforeload : true,
15279         /**
15280          * @event load
15281          * Fires before the load method's callback is called.
15282          * @param {Object} This DataProxy object.
15283          * @param {Object} o The data object.
15284          * @param {Object} arg The callback argument object passed to the load function.
15285          */
15286         load : true,
15287         /**
15288          * @event loadexception
15289          * Fires if an Exception occurs during data retrieval.
15290          * @param {Object} This DataProxy object.
15291          * @param {Object} o The data object.
15292          * @param {Object} arg The callback argument object passed to the load function.
15293          * @param {Object} e The Exception.
15294          */
15295         loadexception : true
15296     });
15297     Roo.data.DataProxy.superclass.constructor.call(this);
15298 };
15299
15300 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15301
15302     /**
15303      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15304      */
15305 /*
15306  * Based on:
15307  * Ext JS Library 1.1.1
15308  * Copyright(c) 2006-2007, Ext JS, LLC.
15309  *
15310  * Originally Released Under LGPL - original licence link has changed is not relivant.
15311  *
15312  * Fork - LGPL
15313  * <script type="text/javascript">
15314  */
15315 /**
15316  * @class Roo.data.MemoryProxy
15317  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15318  * to the Reader when its load method is called.
15319  * @constructor
15320  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15321  */
15322 Roo.data.MemoryProxy = function(data){
15323     if (data.data) {
15324         data = data.data;
15325     }
15326     Roo.data.MemoryProxy.superclass.constructor.call(this);
15327     this.data = data;
15328 };
15329
15330 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15331     
15332     /**
15333      * Load data from the requested source (in this case an in-memory
15334      * data object passed to the constructor), read the data object into
15335      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15336      * process that block using the passed callback.
15337      * @param {Object} params This parameter is not used by the MemoryProxy class.
15338      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15339      * object into a block of Roo.data.Records.
15340      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15341      * The function must be passed <ul>
15342      * <li>The Record block object</li>
15343      * <li>The "arg" argument from the load function</li>
15344      * <li>A boolean success indicator</li>
15345      * </ul>
15346      * @param {Object} scope The scope in which to call the callback
15347      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15348      */
15349     load : function(params, reader, callback, scope, arg){
15350         params = params || {};
15351         var result;
15352         try {
15353             result = reader.readRecords(params.data ? params.data :this.data);
15354         }catch(e){
15355             this.fireEvent("loadexception", this, arg, null, e);
15356             callback.call(scope, null, arg, false);
15357             return;
15358         }
15359         callback.call(scope, result, arg, true);
15360     },
15361     
15362     // private
15363     update : function(params, records){
15364         
15365     }
15366 });/*
15367  * Based on:
15368  * Ext JS Library 1.1.1
15369  * Copyright(c) 2006-2007, Ext JS, LLC.
15370  *
15371  * Originally Released Under LGPL - original licence link has changed is not relivant.
15372  *
15373  * Fork - LGPL
15374  * <script type="text/javascript">
15375  */
15376 /**
15377  * @class Roo.data.HttpProxy
15378  * @extends Roo.data.DataProxy
15379  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15380  * configured to reference a certain URL.<br><br>
15381  * <p>
15382  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15383  * from which the running page was served.<br><br>
15384  * <p>
15385  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15386  * <p>
15387  * Be aware that to enable the browser to parse an XML document, the server must set
15388  * the Content-Type header in the HTTP response to "text/xml".
15389  * @constructor
15390  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15391  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15392  * will be used to make the request.
15393  */
15394 Roo.data.HttpProxy = function(conn){
15395     Roo.data.HttpProxy.superclass.constructor.call(this);
15396     // is conn a conn config or a real conn?
15397     this.conn = conn;
15398     this.useAjax = !conn || !conn.events;
15399   
15400 };
15401
15402 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15403     // thse are take from connection...
15404     
15405     /**
15406      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15407      */
15408     /**
15409      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15410      * extra parameters to each request made by this object. (defaults to undefined)
15411      */
15412     /**
15413      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15414      *  to each request made by this object. (defaults to undefined)
15415      */
15416     /**
15417      * @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)
15418      */
15419     /**
15420      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15421      */
15422      /**
15423      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15424      * @type Boolean
15425      */
15426   
15427
15428     /**
15429      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15430      * @type Boolean
15431      */
15432     /**
15433      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15434      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15435      * a finer-grained basis than the DataProxy events.
15436      */
15437     getConnection : function(){
15438         return this.useAjax ? Roo.Ajax : this.conn;
15439     },
15440
15441     /**
15442      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15443      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15444      * process that block using the passed callback.
15445      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15446      * for the request to the remote server.
15447      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15448      * object into a block of Roo.data.Records.
15449      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15450      * The function must be passed <ul>
15451      * <li>The Record block object</li>
15452      * <li>The "arg" argument from the load function</li>
15453      * <li>A boolean success indicator</li>
15454      * </ul>
15455      * @param {Object} scope The scope in which to call the callback
15456      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15457      */
15458     load : function(params, reader, callback, scope, arg){
15459         if(this.fireEvent("beforeload", this, params) !== false){
15460             var  o = {
15461                 params : params || {},
15462                 request: {
15463                     callback : callback,
15464                     scope : scope,
15465                     arg : arg
15466                 },
15467                 reader: reader,
15468                 callback : this.loadResponse,
15469                 scope: this
15470             };
15471             if(this.useAjax){
15472                 Roo.applyIf(o, this.conn);
15473                 if(this.activeRequest){
15474                     Roo.Ajax.abort(this.activeRequest);
15475                 }
15476                 this.activeRequest = Roo.Ajax.request(o);
15477             }else{
15478                 this.conn.request(o);
15479             }
15480         }else{
15481             callback.call(scope||this, null, arg, false);
15482         }
15483     },
15484
15485     // private
15486     loadResponse : function(o, success, response){
15487         delete this.activeRequest;
15488         if(!success){
15489             this.fireEvent("loadexception", this, o, response);
15490             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15491             return;
15492         }
15493         var result;
15494         try {
15495             result = o.reader.read(response);
15496         }catch(e){
15497             this.fireEvent("loadexception", this, o, response, e);
15498             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15499             return;
15500         }
15501         
15502         this.fireEvent("load", this, o, o.request.arg);
15503         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15504     },
15505
15506     // private
15507     update : function(dataSet){
15508
15509     },
15510
15511     // private
15512     updateResponse : function(dataSet){
15513
15514     }
15515 });/*
15516  * Based on:
15517  * Ext JS Library 1.1.1
15518  * Copyright(c) 2006-2007, Ext JS, LLC.
15519  *
15520  * Originally Released Under LGPL - original licence link has changed is not relivant.
15521  *
15522  * Fork - LGPL
15523  * <script type="text/javascript">
15524  */
15525
15526 /**
15527  * @class Roo.data.ScriptTagProxy
15528  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15529  * other than the originating domain of the running page.<br><br>
15530  * <p>
15531  * <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
15532  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15533  * <p>
15534  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15535  * source code that is used as the source inside a &lt;script> tag.<br><br>
15536  * <p>
15537  * In order for the browser to process the returned data, the server must wrap the data object
15538  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15539  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15540  * depending on whether the callback name was passed:
15541  * <p>
15542  * <pre><code>
15543 boolean scriptTag = false;
15544 String cb = request.getParameter("callback");
15545 if (cb != null) {
15546     scriptTag = true;
15547     response.setContentType("text/javascript");
15548 } else {
15549     response.setContentType("application/x-json");
15550 }
15551 Writer out = response.getWriter();
15552 if (scriptTag) {
15553     out.write(cb + "(");
15554 }
15555 out.print(dataBlock.toJsonString());
15556 if (scriptTag) {
15557     out.write(");");
15558 }
15559 </pre></code>
15560  *
15561  * @constructor
15562  * @param {Object} config A configuration object.
15563  */
15564 Roo.data.ScriptTagProxy = function(config){
15565     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15566     Roo.apply(this, config);
15567     this.head = document.getElementsByTagName("head")[0];
15568 };
15569
15570 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15571
15572 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15573     /**
15574      * @cfg {String} url The URL from which to request the data object.
15575      */
15576     /**
15577      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15578      */
15579     timeout : 30000,
15580     /**
15581      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15582      * the server the name of the callback function set up by the load call to process the returned data object.
15583      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15584      * javascript output which calls this named function passing the data object as its only parameter.
15585      */
15586     callbackParam : "callback",
15587     /**
15588      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15589      * name to the request.
15590      */
15591     nocache : true,
15592
15593     /**
15594      * Load data from the configured URL, read the data object into
15595      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15596      * process that block using the passed callback.
15597      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15598      * for the request to the remote server.
15599      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15600      * object into a block of Roo.data.Records.
15601      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15602      * The function must be passed <ul>
15603      * <li>The Record block object</li>
15604      * <li>The "arg" argument from the load function</li>
15605      * <li>A boolean success indicator</li>
15606      * </ul>
15607      * @param {Object} scope The scope in which to call the callback
15608      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15609      */
15610     load : function(params, reader, callback, scope, arg){
15611         if(this.fireEvent("beforeload", this, params) !== false){
15612
15613             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15614
15615             var url = this.url;
15616             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15617             if(this.nocache){
15618                 url += "&_dc=" + (new Date().getTime());
15619             }
15620             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15621             var trans = {
15622                 id : transId,
15623                 cb : "stcCallback"+transId,
15624                 scriptId : "stcScript"+transId,
15625                 params : params,
15626                 arg : arg,
15627                 url : url,
15628                 callback : callback,
15629                 scope : scope,
15630                 reader : reader
15631             };
15632             var conn = this;
15633
15634             window[trans.cb] = function(o){
15635                 conn.handleResponse(o, trans);
15636             };
15637
15638             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15639
15640             if(this.autoAbort !== false){
15641                 this.abort();
15642             }
15643
15644             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15645
15646             var script = document.createElement("script");
15647             script.setAttribute("src", url);
15648             script.setAttribute("type", "text/javascript");
15649             script.setAttribute("id", trans.scriptId);
15650             this.head.appendChild(script);
15651
15652             this.trans = trans;
15653         }else{
15654             callback.call(scope||this, null, arg, false);
15655         }
15656     },
15657
15658     // private
15659     isLoading : function(){
15660         return this.trans ? true : false;
15661     },
15662
15663     /**
15664      * Abort the current server request.
15665      */
15666     abort : function(){
15667         if(this.isLoading()){
15668             this.destroyTrans(this.trans);
15669         }
15670     },
15671
15672     // private
15673     destroyTrans : function(trans, isLoaded){
15674         this.head.removeChild(document.getElementById(trans.scriptId));
15675         clearTimeout(trans.timeoutId);
15676         if(isLoaded){
15677             window[trans.cb] = undefined;
15678             try{
15679                 delete window[trans.cb];
15680             }catch(e){}
15681         }else{
15682             // if hasn't been loaded, wait for load to remove it to prevent script error
15683             window[trans.cb] = function(){
15684                 window[trans.cb] = undefined;
15685                 try{
15686                     delete window[trans.cb];
15687                 }catch(e){}
15688             };
15689         }
15690     },
15691
15692     // private
15693     handleResponse : function(o, trans){
15694         this.trans = false;
15695         this.destroyTrans(trans, true);
15696         var result;
15697         try {
15698             result = trans.reader.readRecords(o);
15699         }catch(e){
15700             this.fireEvent("loadexception", this, o, trans.arg, e);
15701             trans.callback.call(trans.scope||window, null, trans.arg, false);
15702             return;
15703         }
15704         this.fireEvent("load", this, o, trans.arg);
15705         trans.callback.call(trans.scope||window, result, trans.arg, true);
15706     },
15707
15708     // private
15709     handleFailure : function(trans){
15710         this.trans = false;
15711         this.destroyTrans(trans, false);
15712         this.fireEvent("loadexception", this, null, trans.arg);
15713         trans.callback.call(trans.scope||window, null, trans.arg, false);
15714     }
15715 });/*
15716  * Based on:
15717  * Ext JS Library 1.1.1
15718  * Copyright(c) 2006-2007, Ext JS, LLC.
15719  *
15720  * Originally Released Under LGPL - original licence link has changed is not relivant.
15721  *
15722  * Fork - LGPL
15723  * <script type="text/javascript">
15724  */
15725
15726 /**
15727  * @class Roo.data.JsonReader
15728  * @extends Roo.data.DataReader
15729  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15730  * based on mappings in a provided Roo.data.Record constructor.
15731  * 
15732  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15733  * in the reply previously. 
15734  * 
15735  * <p>
15736  * Example code:
15737  * <pre><code>
15738 var RecordDef = Roo.data.Record.create([
15739     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
15740     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
15741 ]);
15742 var myReader = new Roo.data.JsonReader({
15743     totalProperty: "results",    // The property which contains the total dataset size (optional)
15744     root: "rows",                // The property which contains an Array of row objects
15745     id: "id"                     // The property within each row object that provides an ID for the record (optional)
15746 }, RecordDef);
15747 </code></pre>
15748  * <p>
15749  * This would consume a JSON file like this:
15750  * <pre><code>
15751 { 'results': 2, 'rows': [
15752     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15753     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15754 }
15755 </code></pre>
15756  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15757  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15758  * paged from the remote server.
15759  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15760  * @cfg {String} root name of the property which contains the Array of row objects.
15761  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15762  * @cfg {Array} fields Array of field definition objects
15763  * @constructor
15764  * Create a new JsonReader
15765  * @param {Object} meta Metadata configuration options
15766  * @param {Object} recordType Either an Array of field definition objects,
15767  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15768  */
15769 Roo.data.JsonReader = function(meta, recordType){
15770     
15771     meta = meta || {};
15772     // set some defaults:
15773     Roo.applyIf(meta, {
15774         totalProperty: 'total',
15775         successProperty : 'success',
15776         root : 'data',
15777         id : 'id'
15778     });
15779     
15780     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15781 };
15782 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15783     
15784     readerType : 'Json',
15785     
15786     /**
15787      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15788      * Used by Store query builder to append _requestMeta to params.
15789      * 
15790      */
15791     metaFromRemote : false,
15792     /**
15793      * This method is only used by a DataProxy which has retrieved data from a remote server.
15794      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15795      * @return {Object} data A data block which is used by an Roo.data.Store object as
15796      * a cache of Roo.data.Records.
15797      */
15798     read : function(response){
15799         var json = response.responseText;
15800        
15801         var o = /* eval:var:o */ eval("("+json+")");
15802         if(!o) {
15803             throw {message: "JsonReader.read: Json object not found"};
15804         }
15805         
15806         if(o.metaData){
15807             
15808             delete this.ef;
15809             this.metaFromRemote = true;
15810             this.meta = o.metaData;
15811             this.recordType = Roo.data.Record.create(o.metaData.fields);
15812             this.onMetaChange(this.meta, this.recordType, o);
15813         }
15814         return this.readRecords(o);
15815     },
15816
15817     // private function a store will implement
15818     onMetaChange : function(meta, recordType, o){
15819
15820     },
15821
15822     /**
15823          * @ignore
15824          */
15825     simpleAccess: function(obj, subsc) {
15826         return obj[subsc];
15827     },
15828
15829         /**
15830          * @ignore
15831          */
15832     getJsonAccessor: function(){
15833         var re = /[\[\.]/;
15834         return function(expr) {
15835             try {
15836                 return(re.test(expr))
15837                     ? new Function("obj", "return obj." + expr)
15838                     : function(obj){
15839                         return obj[expr];
15840                     };
15841             } catch(e){}
15842             return Roo.emptyFn;
15843         };
15844     }(),
15845
15846     /**
15847      * Create a data block containing Roo.data.Records from an XML document.
15848      * @param {Object} o An object which contains an Array of row objects in the property specified
15849      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15850      * which contains the total size of the dataset.
15851      * @return {Object} data A data block which is used by an Roo.data.Store object as
15852      * a cache of Roo.data.Records.
15853      */
15854     readRecords : function(o){
15855         /**
15856          * After any data loads, the raw JSON data is available for further custom processing.
15857          * @type Object
15858          */
15859         this.o = o;
15860         var s = this.meta, Record = this.recordType,
15861             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15862
15863 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15864         if (!this.ef) {
15865             if(s.totalProperty) {
15866                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15867                 }
15868                 if(s.successProperty) {
15869                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15870                 }
15871                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15872                 if (s.id) {
15873                         var g = this.getJsonAccessor(s.id);
15874                         this.getId = function(rec) {
15875                                 var r = g(rec);  
15876                                 return (r === undefined || r === "") ? null : r;
15877                         };
15878                 } else {
15879                         this.getId = function(){return null;};
15880                 }
15881             this.ef = [];
15882             for(var jj = 0; jj < fl; jj++){
15883                 f = fi[jj];
15884                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15885                 this.ef[jj] = this.getJsonAccessor(map);
15886             }
15887         }
15888
15889         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15890         if(s.totalProperty){
15891             var vt = parseInt(this.getTotal(o), 10);
15892             if(!isNaN(vt)){
15893                 totalRecords = vt;
15894             }
15895         }
15896         if(s.successProperty){
15897             var vs = this.getSuccess(o);
15898             if(vs === false || vs === 'false'){
15899                 success = false;
15900             }
15901         }
15902         var records = [];
15903         for(var i = 0; i < c; i++){
15904                 var n = root[i];
15905             var values = {};
15906             var id = this.getId(n);
15907             for(var j = 0; j < fl; j++){
15908                 f = fi[j];
15909             var v = this.ef[j](n);
15910             if (!f.convert) {
15911                 Roo.log('missing convert for ' + f.name);
15912                 Roo.log(f);
15913                 continue;
15914             }
15915             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15916             }
15917             var record = new Record(values, id);
15918             record.json = n;
15919             records[i] = record;
15920         }
15921         return {
15922             raw : o,
15923             success : success,
15924             records : records,
15925             totalRecords : totalRecords
15926         };
15927     },
15928     // used when loading children.. @see loadDataFromChildren
15929     toLoadData: function(rec)
15930     {
15931         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15932         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15933         return { data : data, total : data.length };
15934         
15935     }
15936 });/*
15937  * Based on:
15938  * Ext JS Library 1.1.1
15939  * Copyright(c) 2006-2007, Ext JS, LLC.
15940  *
15941  * Originally Released Under LGPL - original licence link has changed is not relivant.
15942  *
15943  * Fork - LGPL
15944  * <script type="text/javascript">
15945  */
15946
15947 /**
15948  * @class Roo.data.ArrayReader
15949  * @extends Roo.data.DataReader
15950  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15951  * Each element of that Array represents a row of data fields. The
15952  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15953  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15954  * <p>
15955  * Example code:.
15956  * <pre><code>
15957 var RecordDef = Roo.data.Record.create([
15958     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15959     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15960 ]);
15961 var myReader = new Roo.data.ArrayReader({
15962     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15963 }, RecordDef);
15964 </code></pre>
15965  * <p>
15966  * This would consume an Array like this:
15967  * <pre><code>
15968 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15969   </code></pre>
15970  
15971  * @constructor
15972  * Create a new JsonReader
15973  * @param {Object} meta Metadata configuration options.
15974  * @param {Object|Array} recordType Either an Array of field definition objects
15975  * 
15976  * @cfg {Array} fields Array of field definition objects
15977  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15978  * as specified to {@link Roo.data.Record#create},
15979  * or an {@link Roo.data.Record} object
15980  *
15981  * 
15982  * created using {@link Roo.data.Record#create}.
15983  */
15984 Roo.data.ArrayReader = function(meta, recordType)
15985 {    
15986     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15987 };
15988
15989 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15990     
15991       /**
15992      * Create a data block containing Roo.data.Records from an XML document.
15993      * @param {Object} o An Array of row objects which represents the dataset.
15994      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15995      * a cache of Roo.data.Records.
15996      */
15997     readRecords : function(o)
15998     {
15999         var sid = this.meta ? this.meta.id : null;
16000         var recordType = this.recordType, fields = recordType.prototype.fields;
16001         var records = [];
16002         var root = o;
16003         for(var i = 0; i < root.length; i++){
16004             var n = root[i];
16005             var values = {};
16006             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16007             for(var j = 0, jlen = fields.length; j < jlen; j++){
16008                 var f = fields.items[j];
16009                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16010                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16011                 v = f.convert(v);
16012                 values[f.name] = v;
16013             }
16014             var record = new recordType(values, id);
16015             record.json = n;
16016             records[records.length] = record;
16017         }
16018         return {
16019             records : records,
16020             totalRecords : records.length
16021         };
16022     },
16023     // used when loading children.. @see loadDataFromChildren
16024     toLoadData: function(rec)
16025     {
16026         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16027         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16028         
16029     }
16030     
16031     
16032 });/*
16033  * - LGPL
16034  * * 
16035  */
16036
16037 /**
16038  * @class Roo.bootstrap.ComboBox
16039  * @extends Roo.bootstrap.TriggerField
16040  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16041  * @cfg {Boolean} append (true|false) default false
16042  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16043  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16044  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16045  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16046  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16047  * @cfg {Boolean} animate default true
16048  * @cfg {Boolean} emptyResultText only for touch device
16049  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16050  * @cfg {String} emptyTitle default ''
16051  * @cfg {Number} width fixed with? experimental
16052  * @constructor
16053  * Create a new ComboBox.
16054  * @param {Object} config Configuration options
16055  */
16056 Roo.bootstrap.ComboBox = function(config){
16057     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16058     this.addEvents({
16059         /**
16060          * @event expand
16061          * Fires when the dropdown list is expanded
16062         * @param {Roo.bootstrap.ComboBox} combo This combo box
16063         */
16064         'expand' : true,
16065         /**
16066          * @event collapse
16067          * Fires when the dropdown list is collapsed
16068         * @param {Roo.bootstrap.ComboBox} combo This combo box
16069         */
16070         'collapse' : true,
16071         /**
16072          * @event beforeselect
16073          * Fires before a list item is selected. Return false to cancel the selection.
16074         * @param {Roo.bootstrap.ComboBox} combo This combo box
16075         * @param {Roo.data.Record} record The data record returned from the underlying store
16076         * @param {Number} index The index of the selected item in the dropdown list
16077         */
16078         'beforeselect' : true,
16079         /**
16080          * @event select
16081          * Fires when a list item is selected
16082         * @param {Roo.bootstrap.ComboBox} combo This combo box
16083         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16084         * @param {Number} index The index of the selected item in the dropdown list
16085         */
16086         'select' : true,
16087         /**
16088          * @event beforequery
16089          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16090          * The event object passed has these properties:
16091         * @param {Roo.bootstrap.ComboBox} combo This combo box
16092         * @param {String} query The query
16093         * @param {Boolean} forceAll true to force "all" query
16094         * @param {Boolean} cancel true to cancel the query
16095         * @param {Object} e The query event object
16096         */
16097         'beforequery': true,
16098          /**
16099          * @event add
16100          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16101         * @param {Roo.bootstrap.ComboBox} combo This combo box
16102         */
16103         'add' : true,
16104         /**
16105          * @event edit
16106          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16107         * @param {Roo.bootstrap.ComboBox} combo This combo box
16108         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16109         */
16110         'edit' : true,
16111         /**
16112          * @event remove
16113          * Fires when the remove value from the combobox array
16114         * @param {Roo.bootstrap.ComboBox} combo This combo box
16115         */
16116         'remove' : true,
16117         /**
16118          * @event afterremove
16119          * Fires when the remove value from the combobox array
16120         * @param {Roo.bootstrap.ComboBox} combo This combo box
16121         */
16122         'afterremove' : true,
16123         /**
16124          * @event specialfilter
16125          * Fires when specialfilter
16126             * @param {Roo.bootstrap.ComboBox} combo This combo box
16127             */
16128         'specialfilter' : true,
16129         /**
16130          * @event tick
16131          * Fires when tick the element
16132             * @param {Roo.bootstrap.ComboBox} combo This combo box
16133             */
16134         'tick' : true,
16135         /**
16136          * @event touchviewdisplay
16137          * Fires when touch view require special display (default is using displayField)
16138             * @param {Roo.bootstrap.ComboBox} combo This combo box
16139             * @param {Object} cfg set html .
16140             */
16141         'touchviewdisplay' : true
16142         
16143     });
16144     
16145     this.item = [];
16146     this.tickItems = [];
16147     
16148     this.selectedIndex = -1;
16149     if(this.mode == 'local'){
16150         if(config.queryDelay === undefined){
16151             this.queryDelay = 10;
16152         }
16153         if(config.minChars === undefined){
16154             this.minChars = 0;
16155         }
16156     }
16157 };
16158
16159 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16160      
16161     /**
16162      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16163      * rendering into an Roo.Editor, defaults to false)
16164      */
16165     /**
16166      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16167      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16168      */
16169     /**
16170      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16171      */
16172     /**
16173      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16174      * the dropdown list (defaults to undefined, with no header element)
16175      */
16176
16177      /**
16178      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16179      */
16180      
16181      /**
16182      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16183      */
16184     listWidth: undefined,
16185     /**
16186      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16187      * mode = 'remote' or 'text' if mode = 'local')
16188      */
16189     displayField: undefined,
16190     
16191     /**
16192      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16193      * mode = 'remote' or 'value' if mode = 'local'). 
16194      * Note: use of a valueField requires the user make a selection
16195      * in order for a value to be mapped.
16196      */
16197     valueField: undefined,
16198     /**
16199      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16200      */
16201     modalTitle : '',
16202     
16203     /**
16204      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16205      * field's data value (defaults to the underlying DOM element's name)
16206      */
16207     hiddenName: undefined,
16208     /**
16209      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16210      */
16211     listClass: '',
16212     /**
16213      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16214      */
16215     selectedClass: 'active',
16216     
16217     /**
16218      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16219      */
16220     shadow:'sides',
16221     /**
16222      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16223      * anchor positions (defaults to 'tl-bl')
16224      */
16225     listAlign: 'tl-bl?',
16226     /**
16227      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16228      */
16229     maxHeight: 300,
16230     /**
16231      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16232      * query specified by the allQuery config option (defaults to 'query')
16233      */
16234     triggerAction: 'query',
16235     /**
16236      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16237      * (defaults to 4, does not apply if editable = false)
16238      */
16239     minChars : 4,
16240     /**
16241      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16242      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16243      */
16244     typeAhead: false,
16245     /**
16246      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16247      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16248      */
16249     queryDelay: 500,
16250     /**
16251      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16252      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16253      */
16254     pageSize: 0,
16255     /**
16256      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16257      * when editable = true (defaults to false)
16258      */
16259     selectOnFocus:false,
16260     /**
16261      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16262      */
16263     queryParam: 'query',
16264     /**
16265      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16266      * when mode = 'remote' (defaults to 'Loading...')
16267      */
16268     loadingText: 'Loading...',
16269     /**
16270      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16271      */
16272     resizable: false,
16273     /**
16274      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16275      */
16276     handleHeight : 8,
16277     /**
16278      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16279      * traditional select (defaults to true)
16280      */
16281     editable: true,
16282     /**
16283      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16284      */
16285     allQuery: '',
16286     /**
16287      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16288      */
16289     mode: 'remote',
16290     /**
16291      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16292      * listWidth has a higher value)
16293      */
16294     minListWidth : 70,
16295     /**
16296      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16297      * allow the user to set arbitrary text into the field (defaults to false)
16298      */
16299     forceSelection:false,
16300     /**
16301      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16302      * if typeAhead = true (defaults to 250)
16303      */
16304     typeAheadDelay : 250,
16305     /**
16306      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16307      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16308      */
16309     valueNotFoundText : undefined,
16310     /**
16311      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16312      */
16313     blockFocus : false,
16314     
16315     /**
16316      * @cfg {Boolean} disableClear Disable showing of clear button.
16317      */
16318     disableClear : false,
16319     /**
16320      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16321      */
16322     alwaysQuery : false,
16323     
16324     /**
16325      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16326      */
16327     multiple : false,
16328     
16329     /**
16330      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16331      */
16332     invalidClass : "has-warning",
16333     
16334     /**
16335      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16336      */
16337     validClass : "has-success",
16338     
16339     /**
16340      * @cfg {Boolean} specialFilter (true|false) special filter default false
16341      */
16342     specialFilter : false,
16343     
16344     /**
16345      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16346      */
16347     mobileTouchView : true,
16348     
16349     /**
16350      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16351      */
16352     useNativeIOS : false,
16353     
16354     /**
16355      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16356      */
16357     mobile_restrict_height : false,
16358     
16359     ios_options : false,
16360     
16361     //private
16362     addicon : false,
16363     editicon: false,
16364     
16365     page: 0,
16366     hasQuery: false,
16367     append: false,
16368     loadNext: false,
16369     autoFocus : true,
16370     tickable : false,
16371     btnPosition : 'right',
16372     triggerList : true,
16373     showToggleBtn : true,
16374     animate : true,
16375     emptyResultText: 'Empty',
16376     triggerText : 'Select',
16377     emptyTitle : '',
16378     width : false,
16379     
16380     // element that contains real text value.. (when hidden is used..)
16381     
16382     getAutoCreate : function()
16383     {   
16384         var cfg = false;
16385         //render
16386         /*
16387          * Render classic select for iso
16388          */
16389         
16390         if(Roo.isIOS && this.useNativeIOS){
16391             cfg = this.getAutoCreateNativeIOS();
16392             return cfg;
16393         }
16394         
16395         /*
16396          * Touch Devices
16397          */
16398         
16399         if(Roo.isTouch && this.mobileTouchView){
16400             cfg = this.getAutoCreateTouchView();
16401             return cfg;;
16402         }
16403         
16404         /*
16405          *  Normal ComboBox
16406          */
16407         if(!this.tickable){
16408             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16409             return cfg;
16410         }
16411         
16412         /*
16413          *  ComboBox with tickable selections
16414          */
16415              
16416         var align = this.labelAlign || this.parentLabelAlign();
16417         
16418         cfg = {
16419             cls : 'form-group roo-combobox-tickable' //input-group
16420         };
16421         
16422         var btn_text_select = '';
16423         var btn_text_done = '';
16424         var btn_text_cancel = '';
16425         
16426         if (this.btn_text_show) {
16427             btn_text_select = 'Select';
16428             btn_text_done = 'Done';
16429             btn_text_cancel = 'Cancel'; 
16430         }
16431         
16432         var buttons = {
16433             tag : 'div',
16434             cls : 'tickable-buttons',
16435             cn : [
16436                 {
16437                     tag : 'button',
16438                     type : 'button',
16439                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16440                     //html : this.triggerText
16441                     html: btn_text_select
16442                 },
16443                 {
16444                     tag : 'button',
16445                     type : 'button',
16446                     name : 'ok',
16447                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16448                     //html : 'Done'
16449                     html: btn_text_done
16450                 },
16451                 {
16452                     tag : 'button',
16453                     type : 'button',
16454                     name : 'cancel',
16455                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16456                     //html : 'Cancel'
16457                     html: btn_text_cancel
16458                 }
16459             ]
16460         };
16461         
16462         if(this.editable){
16463             buttons.cn.unshift({
16464                 tag: 'input',
16465                 cls: 'roo-select2-search-field-input'
16466             });
16467         }
16468         
16469         var _this = this;
16470         
16471         Roo.each(buttons.cn, function(c){
16472             if (_this.size) {
16473                 c.cls += ' btn-' + _this.size;
16474             }
16475
16476             if (_this.disabled) {
16477                 c.disabled = true;
16478             }
16479         });
16480         
16481         var box = {
16482             tag: 'div',
16483             style : 'display: contents',
16484             cn: [
16485                 {
16486                     tag: 'input',
16487                     type : 'hidden',
16488                     cls: 'form-hidden-field'
16489                 },
16490                 {
16491                     tag: 'ul',
16492                     cls: 'roo-select2-choices',
16493                     cn:[
16494                         {
16495                             tag: 'li',
16496                             cls: 'roo-select2-search-field',
16497                             cn: [
16498                                 buttons
16499                             ]
16500                         }
16501                     ]
16502                 }
16503             ]
16504         };
16505         
16506         var combobox = {
16507             cls: 'roo-select2-container input-group roo-select2-container-multi',
16508             cn: [
16509                 
16510                 box
16511 //                {
16512 //                    tag: 'ul',
16513 //                    cls: 'typeahead typeahead-long dropdown-menu',
16514 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16515 //                }
16516             ]
16517         };
16518         
16519         if(this.hasFeedback && !this.allowBlank){
16520             
16521             var feedback = {
16522                 tag: 'span',
16523                 cls: 'glyphicon form-control-feedback'
16524             };
16525
16526             combobox.cn.push(feedback);
16527         }
16528         
16529         
16530         
16531         var indicator = {
16532             tag : 'i',
16533             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16534             tooltip : 'This field is required'
16535         };
16536         if (Roo.bootstrap.version == 4) {
16537             indicator = {
16538                 tag : 'i',
16539                 style : 'display:none'
16540             };
16541         }
16542         if (align ==='left' && this.fieldLabel.length) {
16543             
16544             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16545             
16546             cfg.cn = [
16547                 indicator,
16548                 {
16549                     tag: 'label',
16550                     'for' :  id,
16551                     cls : 'control-label col-form-label',
16552                     html : this.fieldLabel
16553
16554                 },
16555                 {
16556                     cls : "", 
16557                     cn: [
16558                         combobox
16559                     ]
16560                 }
16561
16562             ];
16563             
16564             var labelCfg = cfg.cn[1];
16565             var contentCfg = cfg.cn[2];
16566             
16567
16568             if(this.indicatorpos == 'right'){
16569                 
16570                 cfg.cn = [
16571                     {
16572                         tag: 'label',
16573                         'for' :  id,
16574                         cls : 'control-label col-form-label',
16575                         cn : [
16576                             {
16577                                 tag : 'span',
16578                                 html : this.fieldLabel
16579                             },
16580                             indicator
16581                         ]
16582                     },
16583                     {
16584                         cls : "",
16585                         cn: [
16586                             combobox
16587                         ]
16588                     }
16589
16590                 ];
16591                 
16592                 
16593                 
16594                 labelCfg = cfg.cn[0];
16595                 contentCfg = cfg.cn[1];
16596             
16597             }
16598             
16599             if(this.labelWidth > 12){
16600                 labelCfg.style = "width: " + this.labelWidth + 'px';
16601             }
16602             if(this.width * 1 > 0){
16603                 contentCfg.style = "width: " + this.width + 'px';
16604             }
16605             if(this.labelWidth < 13 && this.labelmd == 0){
16606                 this.labelmd = this.labelWidth;
16607             }
16608             
16609             if(this.labellg > 0){
16610                 labelCfg.cls += ' col-lg-' + this.labellg;
16611                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16612             }
16613             
16614             if(this.labelmd > 0){
16615                 labelCfg.cls += ' col-md-' + this.labelmd;
16616                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16617             }
16618             
16619             if(this.labelsm > 0){
16620                 labelCfg.cls += ' col-sm-' + this.labelsm;
16621                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16622             }
16623             
16624             if(this.labelxs > 0){
16625                 labelCfg.cls += ' col-xs-' + this.labelxs;
16626                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16627             }
16628                 
16629                 
16630         } else if ( this.fieldLabel.length) {
16631 //                Roo.log(" label");
16632                  cfg.cn = [
16633                    indicator,
16634                     {
16635                         tag: 'label',
16636                         //cls : 'input-group-addon',
16637                         html : this.fieldLabel
16638                     },
16639                     combobox
16640                 ];
16641                 
16642                 if(this.indicatorpos == 'right'){
16643                     cfg.cn = [
16644                         {
16645                             tag: 'label',
16646                             //cls : 'input-group-addon',
16647                             html : this.fieldLabel
16648                         },
16649                         indicator,
16650                         combobox
16651                     ];
16652                     
16653                 }
16654
16655         } else {
16656             
16657 //                Roo.log(" no label && no align");
16658                 cfg = combobox
16659                      
16660                 
16661         }
16662          
16663         var settings=this;
16664         ['xs','sm','md','lg'].map(function(size){
16665             if (settings[size]) {
16666                 cfg.cls += ' col-' + size + '-' + settings[size];
16667             }
16668         });
16669         
16670         return cfg;
16671         
16672     },
16673     
16674     _initEventsCalled : false,
16675     
16676     // private
16677     initEvents: function()
16678     {   
16679         if (this._initEventsCalled) { // as we call render... prevent looping...
16680             return;
16681         }
16682         this._initEventsCalled = true;
16683         
16684         if (!this.store) {
16685             throw "can not find store for combo";
16686         }
16687         
16688         this.indicator = this.indicatorEl();
16689         
16690         this.store = Roo.factory(this.store, Roo.data);
16691         this.store.parent = this;
16692         
16693         // if we are building from html. then this element is so complex, that we can not really
16694         // use the rendered HTML.
16695         // so we have to trash and replace the previous code.
16696         if (Roo.XComponent.build_from_html) {
16697             // remove this element....
16698             var e = this.el.dom, k=0;
16699             while (e ) { e = e.previousSibling;  ++k;}
16700
16701             this.el.remove();
16702             
16703             this.el=false;
16704             this.rendered = false;
16705             
16706             this.render(this.parent().getChildContainer(true), k);
16707         }
16708         
16709         if(Roo.isIOS && this.useNativeIOS){
16710             this.initIOSView();
16711             return;
16712         }
16713         
16714         /*
16715          * Touch Devices
16716          */
16717         
16718         if(Roo.isTouch && this.mobileTouchView){
16719             this.initTouchView();
16720             return;
16721         }
16722         
16723         if(this.tickable){
16724             this.initTickableEvents();
16725             return;
16726         }
16727         
16728         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16729         
16730         if(this.hiddenName){
16731             
16732             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16733             
16734             this.hiddenField.dom.value =
16735                 this.hiddenValue !== undefined ? this.hiddenValue :
16736                 this.value !== undefined ? this.value : '';
16737
16738             // prevent input submission
16739             this.el.dom.removeAttribute('name');
16740             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16741              
16742              
16743         }
16744         //if(Roo.isGecko){
16745         //    this.el.dom.setAttribute('autocomplete', 'off');
16746         //}
16747         
16748         var cls = 'x-combo-list';
16749         
16750         //this.list = new Roo.Layer({
16751         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16752         //});
16753         
16754         var _this = this;
16755         
16756         (function(){
16757             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16758             _this.list.setWidth(lw);
16759         }).defer(100);
16760         
16761         this.list.on('mouseover', this.onViewOver, this);
16762         this.list.on('mousemove', this.onViewMove, this);
16763         this.list.on('scroll', this.onViewScroll, this);
16764         
16765         /*
16766         this.list.swallowEvent('mousewheel');
16767         this.assetHeight = 0;
16768
16769         if(this.title){
16770             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16771             this.assetHeight += this.header.getHeight();
16772         }
16773
16774         this.innerList = this.list.createChild({cls:cls+'-inner'});
16775         this.innerList.on('mouseover', this.onViewOver, this);
16776         this.innerList.on('mousemove', this.onViewMove, this);
16777         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16778         
16779         if(this.allowBlank && !this.pageSize && !this.disableClear){
16780             this.footer = this.list.createChild({cls:cls+'-ft'});
16781             this.pageTb = new Roo.Toolbar(this.footer);
16782            
16783         }
16784         if(this.pageSize){
16785             this.footer = this.list.createChild({cls:cls+'-ft'});
16786             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16787                     {pageSize: this.pageSize});
16788             
16789         }
16790         
16791         if (this.pageTb && this.allowBlank && !this.disableClear) {
16792             var _this = this;
16793             this.pageTb.add(new Roo.Toolbar.Fill(), {
16794                 cls: 'x-btn-icon x-btn-clear',
16795                 text: '&#160;',
16796                 handler: function()
16797                 {
16798                     _this.collapse();
16799                     _this.clearValue();
16800                     _this.onSelect(false, -1);
16801                 }
16802             });
16803         }
16804         if (this.footer) {
16805             this.assetHeight += this.footer.getHeight();
16806         }
16807         */
16808             
16809         if(!this.tpl){
16810             this.tpl = Roo.bootstrap.version == 4 ?
16811                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16812                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16813         }
16814
16815         this.view = new Roo.View(this.list, this.tpl, {
16816             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16817         });
16818         //this.view.wrapEl.setDisplayed(false);
16819         this.view.on('click', this.onViewClick, this);
16820         
16821         
16822         this.store.on('beforeload', this.onBeforeLoad, this);
16823         this.store.on('load', this.onLoad, this);
16824         this.store.on('loadexception', this.onLoadException, this);
16825         /*
16826         if(this.resizable){
16827             this.resizer = new Roo.Resizable(this.list,  {
16828                pinned:true, handles:'se'
16829             });
16830             this.resizer.on('resize', function(r, w, h){
16831                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16832                 this.listWidth = w;
16833                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16834                 this.restrictHeight();
16835             }, this);
16836             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16837         }
16838         */
16839         if(!this.editable){
16840             this.editable = true;
16841             this.setEditable(false);
16842         }
16843         
16844         /*
16845         
16846         if (typeof(this.events.add.listeners) != 'undefined') {
16847             
16848             this.addicon = this.wrap.createChild(
16849                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16850        
16851             this.addicon.on('click', function(e) {
16852                 this.fireEvent('add', this);
16853             }, this);
16854         }
16855         if (typeof(this.events.edit.listeners) != 'undefined') {
16856             
16857             this.editicon = this.wrap.createChild(
16858                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16859             if (this.addicon) {
16860                 this.editicon.setStyle('margin-left', '40px');
16861             }
16862             this.editicon.on('click', function(e) {
16863                 
16864                 // we fire even  if inothing is selected..
16865                 this.fireEvent('edit', this, this.lastData );
16866                 
16867             }, this);
16868         }
16869         */
16870         
16871         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16872             "up" : function(e){
16873                 this.inKeyMode = true;
16874                 this.selectPrev();
16875             },
16876
16877             "down" : function(e){
16878                 if(!this.isExpanded()){
16879                     this.onTriggerClick();
16880                 }else{
16881                     this.inKeyMode = true;
16882                     this.selectNext();
16883                 }
16884             },
16885
16886             "enter" : function(e){
16887 //                this.onViewClick();
16888                 //return true;
16889                 this.collapse();
16890                 
16891                 if(this.fireEvent("specialkey", this, e)){
16892                     this.onViewClick(false);
16893                 }
16894                 
16895                 return true;
16896             },
16897
16898             "esc" : function(e){
16899                 this.collapse();
16900             },
16901
16902             "tab" : function(e){
16903                 this.collapse();
16904                 
16905                 if(this.fireEvent("specialkey", this, e)){
16906                     this.onViewClick(false);
16907                 }
16908                 
16909                 return true;
16910             },
16911
16912             scope : this,
16913
16914             doRelay : function(foo, bar, hname){
16915                 if(hname == 'down' || this.scope.isExpanded()){
16916                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16917                 }
16918                 return true;
16919             },
16920
16921             forceKeyDown: true
16922         });
16923         
16924         
16925         this.queryDelay = Math.max(this.queryDelay || 10,
16926                 this.mode == 'local' ? 10 : 250);
16927         
16928         
16929         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16930         
16931         if(this.typeAhead){
16932             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16933         }
16934         if(this.editable !== false){
16935             this.inputEl().on("keyup", this.onKeyUp, this);
16936         }
16937         if(this.forceSelection){
16938             this.inputEl().on('blur', this.doForce, this);
16939         }
16940         
16941         if(this.multiple){
16942             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16943             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16944         }
16945     },
16946     
16947     initTickableEvents: function()
16948     {   
16949         this.createList();
16950         
16951         if(this.hiddenName){
16952             
16953             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16954             
16955             this.hiddenField.dom.value =
16956                 this.hiddenValue !== undefined ? this.hiddenValue :
16957                 this.value !== undefined ? this.value : '';
16958
16959             // prevent input submission
16960             this.el.dom.removeAttribute('name');
16961             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16962              
16963              
16964         }
16965         
16966 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16967         
16968         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16969         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16970         if(this.triggerList){
16971             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16972         }
16973          
16974         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16975         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16976         
16977         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16978         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16979         
16980         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16981         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16982         
16983         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16984         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16985         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16986         
16987         this.okBtn.hide();
16988         this.cancelBtn.hide();
16989         
16990         var _this = this;
16991         
16992         (function(){
16993             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16994             _this.list.setWidth(lw);
16995         }).defer(100);
16996         
16997         this.list.on('mouseover', this.onViewOver, this);
16998         this.list.on('mousemove', this.onViewMove, this);
16999         
17000         this.list.on('scroll', this.onViewScroll, this);
17001         
17002         if(!this.tpl){
17003             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17004                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17005         }
17006
17007         this.view = new Roo.View(this.list, this.tpl, {
17008             singleSelect:true,
17009             tickable:true,
17010             parent:this,
17011             store: this.store,
17012             selectedClass: this.selectedClass
17013         });
17014         
17015         //this.view.wrapEl.setDisplayed(false);
17016         this.view.on('click', this.onViewClick, this);
17017         
17018         
17019         
17020         this.store.on('beforeload', this.onBeforeLoad, this);
17021         this.store.on('load', this.onLoad, this);
17022         this.store.on('loadexception', this.onLoadException, this);
17023         
17024         if(this.editable){
17025             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17026                 "up" : function(e){
17027                     this.inKeyMode = true;
17028                     this.selectPrev();
17029                 },
17030
17031                 "down" : function(e){
17032                     this.inKeyMode = true;
17033                     this.selectNext();
17034                 },
17035
17036                 "enter" : function(e){
17037                     if(this.fireEvent("specialkey", this, e)){
17038                         this.onViewClick(false);
17039                     }
17040                     
17041                     return true;
17042                 },
17043
17044                 "esc" : function(e){
17045                     this.onTickableFooterButtonClick(e, false, false);
17046                 },
17047
17048                 "tab" : function(e){
17049                     this.fireEvent("specialkey", this, e);
17050                     
17051                     this.onTickableFooterButtonClick(e, false, false);
17052                     
17053                     return true;
17054                 },
17055
17056                 scope : this,
17057
17058                 doRelay : function(e, fn, key){
17059                     if(this.scope.isExpanded()){
17060                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17061                     }
17062                     return true;
17063                 },
17064
17065                 forceKeyDown: true
17066             });
17067         }
17068         
17069         this.queryDelay = Math.max(this.queryDelay || 10,
17070                 this.mode == 'local' ? 10 : 250);
17071         
17072         
17073         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17074         
17075         if(this.typeAhead){
17076             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17077         }
17078         
17079         if(this.editable !== false){
17080             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17081         }
17082         
17083         this.indicator = this.indicatorEl();
17084         
17085         if(this.indicator){
17086             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17087             this.indicator.hide();
17088         }
17089         
17090     },
17091
17092     onDestroy : function(){
17093         if(this.view){
17094             this.view.setStore(null);
17095             this.view.el.removeAllListeners();
17096             this.view.el.remove();
17097             this.view.purgeListeners();
17098         }
17099         if(this.list){
17100             this.list.dom.innerHTML  = '';
17101         }
17102         
17103         if(this.store){
17104             this.store.un('beforeload', this.onBeforeLoad, this);
17105             this.store.un('load', this.onLoad, this);
17106             this.store.un('loadexception', this.onLoadException, this);
17107         }
17108         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17109     },
17110
17111     // private
17112     fireKey : function(e){
17113         if(e.isNavKeyPress() && !this.list.isVisible()){
17114             this.fireEvent("specialkey", this, e);
17115         }
17116     },
17117
17118     // private
17119     onResize: function(w, h)
17120     {
17121         
17122         
17123 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17124 //        
17125 //        if(typeof w != 'number'){
17126 //            // we do not handle it!?!?
17127 //            return;
17128 //        }
17129 //        var tw = this.trigger.getWidth();
17130 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17131 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17132 //        var x = w - tw;
17133 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17134 //            
17135 //        //this.trigger.setStyle('left', x+'px');
17136 //        
17137 //        if(this.list && this.listWidth === undefined){
17138 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17139 //            this.list.setWidth(lw);
17140 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17141 //        }
17142         
17143     
17144         
17145     },
17146
17147     /**
17148      * Allow or prevent the user from directly editing the field text.  If false is passed,
17149      * the user will only be able to select from the items defined in the dropdown list.  This method
17150      * is the runtime equivalent of setting the 'editable' config option at config time.
17151      * @param {Boolean} value True to allow the user to directly edit the field text
17152      */
17153     setEditable : function(value){
17154         if(value == this.editable){
17155             return;
17156         }
17157         this.editable = value;
17158         if(!value){
17159             this.inputEl().dom.setAttribute('readOnly', true);
17160             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17161             this.inputEl().addClass('x-combo-noedit');
17162         }else{
17163             this.inputEl().dom.removeAttribute('readOnly');
17164             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17165             this.inputEl().removeClass('x-combo-noedit');
17166         }
17167     },
17168
17169     // private
17170     
17171     onBeforeLoad : function(combo,opts){
17172         if(!this.hasFocus){
17173             return;
17174         }
17175          if (!opts.add) {
17176             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17177          }
17178         this.restrictHeight();
17179         this.selectedIndex = -1;
17180     },
17181
17182     // private
17183     onLoad : function(){
17184         
17185         this.hasQuery = false;
17186         
17187         if(!this.hasFocus){
17188             return;
17189         }
17190         
17191         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17192             this.loading.hide();
17193         }
17194         
17195         if(this.store.getCount() > 0){
17196             
17197             this.expand();
17198             this.restrictHeight();
17199             if(this.lastQuery == this.allQuery){
17200                 if(this.editable && !this.tickable){
17201                     this.inputEl().dom.select();
17202                 }
17203                 
17204                 if(
17205                     !this.selectByValue(this.value, true) &&
17206                     this.autoFocus && 
17207                     (
17208                         !this.store.lastOptions ||
17209                         typeof(this.store.lastOptions.add) == 'undefined' || 
17210                         this.store.lastOptions.add != true
17211                     )
17212                 ){
17213                     this.select(0, true);
17214                 }
17215             }else{
17216                 if(this.autoFocus){
17217                     this.selectNext();
17218                 }
17219                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17220                     this.taTask.delay(this.typeAheadDelay);
17221                 }
17222             }
17223         }else{
17224             this.onEmptyResults();
17225         }
17226         
17227         //this.el.focus();
17228     },
17229     // private
17230     onLoadException : function()
17231     {
17232         this.hasQuery = false;
17233         
17234         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17235             this.loading.hide();
17236         }
17237         
17238         if(this.tickable && this.editable){
17239             return;
17240         }
17241         
17242         this.collapse();
17243         // only causes errors at present
17244         //Roo.log(this.store.reader.jsonData);
17245         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17246             // fixme
17247             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17248         //}
17249         
17250         
17251     },
17252     // private
17253     onTypeAhead : function(){
17254         if(this.store.getCount() > 0){
17255             var r = this.store.getAt(0);
17256             var newValue = r.data[this.displayField];
17257             var len = newValue.length;
17258             var selStart = this.getRawValue().length;
17259             
17260             if(selStart != len){
17261                 this.setRawValue(newValue);
17262                 this.selectText(selStart, newValue.length);
17263             }
17264         }
17265     },
17266
17267     // private
17268     onSelect : function(record, index){
17269         
17270         if(this.fireEvent('beforeselect', this, record, index) !== false){
17271         
17272             this.setFromData(index > -1 ? record.data : false);
17273             
17274             this.collapse();
17275             this.fireEvent('select', this, record, index);
17276         }
17277     },
17278
17279     /**
17280      * Returns the currently selected field value or empty string if no value is set.
17281      * @return {String} value The selected value
17282      */
17283     getValue : function()
17284     {
17285         if(Roo.isIOS && this.useNativeIOS){
17286             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17287         }
17288         
17289         if(this.multiple){
17290             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17291         }
17292         
17293         if(this.valueField){
17294             return typeof this.value != 'undefined' ? this.value : '';
17295         }else{
17296             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17297         }
17298     },
17299     
17300     getRawValue : function()
17301     {
17302         if(Roo.isIOS && this.useNativeIOS){
17303             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17304         }
17305         
17306         var v = this.inputEl().getValue();
17307         
17308         return v;
17309     },
17310
17311     /**
17312      * Clears any text/value currently set in the field
17313      */
17314     clearValue : function(){
17315         
17316         if(this.hiddenField){
17317             this.hiddenField.dom.value = '';
17318         }
17319         this.value = '';
17320         this.setRawValue('');
17321         this.lastSelectionText = '';
17322         this.lastData = false;
17323         
17324         var close = this.closeTriggerEl();
17325         
17326         if(close){
17327             close.hide();
17328         }
17329         
17330         this.validate();
17331         
17332     },
17333
17334     /**
17335      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17336      * will be displayed in the field.  If the value does not match the data value of an existing item,
17337      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17338      * Otherwise the field will be blank (although the value will still be set).
17339      * @param {String} value The value to match
17340      */
17341     setValue : function(v)
17342     {
17343         if(Roo.isIOS && this.useNativeIOS){
17344             this.setIOSValue(v);
17345             return;
17346         }
17347         
17348         if(this.multiple){
17349             this.syncValue();
17350             return;
17351         }
17352         
17353         var text = v;
17354         if(this.valueField){
17355             var r = this.findRecord(this.valueField, v);
17356             if(r){
17357                 text = r.data[this.displayField];
17358             }else if(this.valueNotFoundText !== undefined){
17359                 text = this.valueNotFoundText;
17360             }
17361         }
17362         this.lastSelectionText = text;
17363         if(this.hiddenField){
17364             this.hiddenField.dom.value = v;
17365         }
17366         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17367         this.value = v;
17368         
17369         var close = this.closeTriggerEl();
17370         
17371         if(close){
17372             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17373         }
17374         
17375         this.validate();
17376     },
17377     /**
17378      * @property {Object} the last set data for the element
17379      */
17380     
17381     lastData : false,
17382     /**
17383      * Sets the value of the field based on a object which is related to the record format for the store.
17384      * @param {Object} value the value to set as. or false on reset?
17385      */
17386     setFromData : function(o){
17387         
17388         if(this.multiple){
17389             this.addItem(o);
17390             return;
17391         }
17392             
17393         var dv = ''; // display value
17394         var vv = ''; // value value..
17395         this.lastData = o;
17396         if (this.displayField) {
17397             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17398         } else {
17399             // this is an error condition!!!
17400             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17401         }
17402         
17403         if(this.valueField){
17404             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17405         }
17406         
17407         var close = this.closeTriggerEl();
17408         
17409         if(close){
17410             if(dv.length || vv * 1 > 0){
17411                 close.show() ;
17412                 this.blockFocus=true;
17413             } else {
17414                 close.hide();
17415             }             
17416         }
17417         
17418         if(this.hiddenField){
17419             this.hiddenField.dom.value = vv;
17420             
17421             this.lastSelectionText = dv;
17422             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17423             this.value = vv;
17424             return;
17425         }
17426         // no hidden field.. - we store the value in 'value', but still display
17427         // display field!!!!
17428         this.lastSelectionText = dv;
17429         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17430         this.value = vv;
17431         
17432         
17433         
17434     },
17435     // private
17436     reset : function(){
17437         // overridden so that last data is reset..
17438         
17439         if(this.multiple){
17440             this.clearItem();
17441             return;
17442         }
17443         
17444         this.setValue(this.originalValue);
17445         //this.clearInvalid();
17446         this.lastData = false;
17447         if (this.view) {
17448             this.view.clearSelections();
17449         }
17450         
17451         this.validate();
17452     },
17453     // private
17454     findRecord : function(prop, value){
17455         var record;
17456         if(this.store.getCount() > 0){
17457             this.store.each(function(r){
17458                 if(r.data[prop] == value){
17459                     record = r;
17460                     return false;
17461                 }
17462                 return true;
17463             });
17464         }
17465         return record;
17466     },
17467     
17468     getName: function()
17469     {
17470         // returns hidden if it's set..
17471         if (!this.rendered) {return ''};
17472         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17473         
17474     },
17475     // private
17476     onViewMove : function(e, t){
17477         this.inKeyMode = false;
17478     },
17479
17480     // private
17481     onViewOver : function(e, t){
17482         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17483             return;
17484         }
17485         var item = this.view.findItemFromChild(t);
17486         
17487         if(item){
17488             var index = this.view.indexOf(item);
17489             this.select(index, false);
17490         }
17491     },
17492
17493     // private
17494     onViewClick : function(view, doFocus, el, e)
17495     {
17496         var index = this.view.getSelectedIndexes()[0];
17497         
17498         var r = this.store.getAt(index);
17499         
17500         if(this.tickable){
17501             
17502             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17503                 return;
17504             }
17505             
17506             var rm = false;
17507             var _this = this;
17508             
17509             Roo.each(this.tickItems, function(v,k){
17510                 
17511                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17512                     Roo.log(v);
17513                     _this.tickItems.splice(k, 1);
17514                     
17515                     if(typeof(e) == 'undefined' && view == false){
17516                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17517                     }
17518                     
17519                     rm = true;
17520                     return;
17521                 }
17522             });
17523             
17524             if(rm){
17525                 return;
17526             }
17527             
17528             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17529                 this.tickItems.push(r.data);
17530             }
17531             
17532             if(typeof(e) == 'undefined' && view == false){
17533                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17534             }
17535                     
17536             return;
17537         }
17538         
17539         if(r){
17540             this.onSelect(r, index);
17541         }
17542         if(doFocus !== false && !this.blockFocus){
17543             this.inputEl().focus();
17544         }
17545     },
17546
17547     // private
17548     restrictHeight : function(){
17549         //this.innerList.dom.style.height = '';
17550         //var inner = this.innerList.dom;
17551         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17552         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17553         //this.list.beginUpdate();
17554         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17555         this.list.alignTo(this.inputEl(), this.listAlign);
17556         this.list.alignTo(this.inputEl(), this.listAlign);
17557         //this.list.endUpdate();
17558     },
17559
17560     // private
17561     onEmptyResults : function(){
17562         
17563         if(this.tickable && this.editable){
17564             this.hasFocus = false;
17565             this.restrictHeight();
17566             return;
17567         }
17568         
17569         this.collapse();
17570     },
17571
17572     /**
17573      * Returns true if the dropdown list is expanded, else false.
17574      */
17575     isExpanded : function(){
17576         return this.list.isVisible();
17577     },
17578
17579     /**
17580      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17581      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17582      * @param {String} value The data value of the item to select
17583      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17584      * selected item if it is not currently in view (defaults to true)
17585      * @return {Boolean} True if the value matched an item in the list, else false
17586      */
17587     selectByValue : function(v, scrollIntoView){
17588         if(v !== undefined && v !== null){
17589             var r = this.findRecord(this.valueField || this.displayField, v);
17590             if(r){
17591                 this.select(this.store.indexOf(r), scrollIntoView);
17592                 return true;
17593             }
17594         }
17595         return false;
17596     },
17597
17598     /**
17599      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17600      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17601      * @param {Number} index The zero-based index of the list item to select
17602      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17603      * selected item if it is not currently in view (defaults to true)
17604      */
17605     select : function(index, scrollIntoView){
17606         this.selectedIndex = index;
17607         this.view.select(index);
17608         if(scrollIntoView !== false){
17609             var el = this.view.getNode(index);
17610             /*
17611              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17612              */
17613             if(el){
17614                 this.list.scrollChildIntoView(el, false);
17615             }
17616         }
17617     },
17618
17619     // private
17620     selectNext : function(){
17621         var ct = this.store.getCount();
17622         if(ct > 0){
17623             if(this.selectedIndex == -1){
17624                 this.select(0);
17625             }else if(this.selectedIndex < ct-1){
17626                 this.select(this.selectedIndex+1);
17627             }
17628         }
17629     },
17630
17631     // private
17632     selectPrev : function(){
17633         var ct = this.store.getCount();
17634         if(ct > 0){
17635             if(this.selectedIndex == -1){
17636                 this.select(0);
17637             }else if(this.selectedIndex != 0){
17638                 this.select(this.selectedIndex-1);
17639             }
17640         }
17641     },
17642
17643     // private
17644     onKeyUp : function(e){
17645         if(this.editable !== false && !e.isSpecialKey()){
17646             this.lastKey = e.getKey();
17647             this.dqTask.delay(this.queryDelay);
17648         }
17649     },
17650
17651     // private
17652     validateBlur : function(){
17653         return !this.list || !this.list.isVisible();   
17654     },
17655
17656     // private
17657     initQuery : function(){
17658         
17659         var v = this.getRawValue();
17660         
17661         if(this.tickable && this.editable){
17662             v = this.tickableInputEl().getValue();
17663         }
17664         
17665         this.doQuery(v);
17666     },
17667
17668     // private
17669     doForce : function(){
17670         if(this.inputEl().dom.value.length > 0){
17671             this.inputEl().dom.value =
17672                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17673              
17674         }
17675     },
17676
17677     /**
17678      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17679      * query allowing the query action to be canceled if needed.
17680      * @param {String} query The SQL query to execute
17681      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17682      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17683      * saved in the current store (defaults to false)
17684      */
17685     doQuery : function(q, forceAll){
17686         
17687         if(q === undefined || q === null){
17688             q = '';
17689         }
17690         var qe = {
17691             query: q,
17692             forceAll: forceAll,
17693             combo: this,
17694             cancel:false
17695         };
17696         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17697             return false;
17698         }
17699         q = qe.query;
17700         
17701         forceAll = qe.forceAll;
17702         if(forceAll === true || (q.length >= this.minChars)){
17703             
17704             this.hasQuery = true;
17705             
17706             if(this.lastQuery != q || this.alwaysQuery){
17707                 this.lastQuery = q;
17708                 if(this.mode == 'local'){
17709                     this.selectedIndex = -1;
17710                     if(forceAll){
17711                         this.store.clearFilter();
17712                     }else{
17713                         
17714                         if(this.specialFilter){
17715                             this.fireEvent('specialfilter', this);
17716                             this.onLoad();
17717                             return;
17718                         }
17719                         
17720                         this.store.filter(this.displayField, q);
17721                     }
17722                     
17723                     this.store.fireEvent("datachanged", this.store);
17724                     
17725                     this.onLoad();
17726                     
17727                     
17728                 }else{
17729                     
17730                     this.store.baseParams[this.queryParam] = q;
17731                     
17732                     var options = {params : this.getParams(q)};
17733                     
17734                     if(this.loadNext){
17735                         options.add = true;
17736                         options.params.start = this.page * this.pageSize;
17737                     }
17738                     
17739                     this.store.load(options);
17740                     
17741                     /*
17742                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
17743                      *  we should expand the list on onLoad
17744                      *  so command out it
17745                      */
17746 //                    this.expand();
17747                 }
17748             }else{
17749                 this.selectedIndex = -1;
17750                 this.onLoad();   
17751             }
17752         }
17753         
17754         this.loadNext = false;
17755     },
17756     
17757     // private
17758     getParams : function(q){
17759         var p = {};
17760         //p[this.queryParam] = q;
17761         
17762         if(this.pageSize){
17763             p.start = 0;
17764             p.limit = this.pageSize;
17765         }
17766         return p;
17767     },
17768
17769     /**
17770      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17771      */
17772     collapse : function(){
17773         if(!this.isExpanded()){
17774             return;
17775         }
17776         
17777         this.list.hide();
17778         
17779         this.hasFocus = false;
17780         
17781         if(this.tickable){
17782             this.okBtn.hide();
17783             this.cancelBtn.hide();
17784             this.trigger.show();
17785             
17786             if(this.editable){
17787                 this.tickableInputEl().dom.value = '';
17788                 this.tickableInputEl().blur();
17789             }
17790             
17791         }
17792         
17793         Roo.get(document).un('mousedown', this.collapseIf, this);
17794         Roo.get(document).un('mousewheel', this.collapseIf, this);
17795         if (!this.editable) {
17796             Roo.get(document).un('keydown', this.listKeyPress, this);
17797         }
17798         this.fireEvent('collapse', this);
17799         
17800         this.validate();
17801     },
17802
17803     // private
17804     collapseIf : function(e){
17805         var in_combo  = e.within(this.el);
17806         var in_list =  e.within(this.list);
17807         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17808         
17809         if (in_combo || in_list || is_list) {
17810             //e.stopPropagation();
17811             return;
17812         }
17813         
17814         if(this.tickable){
17815             this.onTickableFooterButtonClick(e, false, false);
17816         }
17817
17818         this.collapse();
17819         
17820     },
17821
17822     /**
17823      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17824      */
17825     expand : function(){
17826        
17827         if(this.isExpanded() || !this.hasFocus){
17828             return;
17829         }
17830         
17831         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17832         this.list.setWidth(lw);
17833         
17834         Roo.log('expand');
17835         
17836         this.list.show();
17837         
17838         this.restrictHeight();
17839         
17840         if(this.tickable){
17841             
17842             this.tickItems = Roo.apply([], this.item);
17843             
17844             this.okBtn.show();
17845             this.cancelBtn.show();
17846             this.trigger.hide();
17847             
17848             if(this.editable){
17849                 this.tickableInputEl().focus();
17850             }
17851             
17852         }
17853         
17854         Roo.get(document).on('mousedown', this.collapseIf, this);
17855         Roo.get(document).on('mousewheel', this.collapseIf, this);
17856         if (!this.editable) {
17857             Roo.get(document).on('keydown', this.listKeyPress, this);
17858         }
17859         
17860         this.fireEvent('expand', this);
17861     },
17862
17863     // private
17864     // Implements the default empty TriggerField.onTriggerClick function
17865     onTriggerClick : function(e)
17866     {
17867         Roo.log('trigger click');
17868         
17869         if(this.disabled || !this.triggerList){
17870             return;
17871         }
17872         
17873         this.page = 0;
17874         this.loadNext = false;
17875         
17876         if(this.isExpanded()){
17877             this.collapse();
17878             if (!this.blockFocus) {
17879                 this.inputEl().focus();
17880             }
17881             
17882         }else {
17883             this.hasFocus = true;
17884             if(this.triggerAction == 'all') {
17885                 this.doQuery(this.allQuery, true);
17886             } else {
17887                 this.doQuery(this.getRawValue());
17888             }
17889             if (!this.blockFocus) {
17890                 this.inputEl().focus();
17891             }
17892         }
17893     },
17894     
17895     onTickableTriggerClick : function(e)
17896     {
17897         if(this.disabled){
17898             return;
17899         }
17900         
17901         this.page = 0;
17902         this.loadNext = false;
17903         this.hasFocus = true;
17904         
17905         if(this.triggerAction == 'all') {
17906             this.doQuery(this.allQuery, true);
17907         } else {
17908             this.doQuery(this.getRawValue());
17909         }
17910     },
17911     
17912     onSearchFieldClick : function(e)
17913     {
17914         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17915             this.onTickableFooterButtonClick(e, false, false);
17916             return;
17917         }
17918         
17919         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17920             return;
17921         }
17922         
17923         this.page = 0;
17924         this.loadNext = false;
17925         this.hasFocus = true;
17926         
17927         if(this.triggerAction == 'all') {
17928             this.doQuery(this.allQuery, true);
17929         } else {
17930             this.doQuery(this.getRawValue());
17931         }
17932     },
17933     
17934     listKeyPress : function(e)
17935     {
17936         //Roo.log('listkeypress');
17937         // scroll to first matching element based on key pres..
17938         if (e.isSpecialKey()) {
17939             return false;
17940         }
17941         var k = String.fromCharCode(e.getKey()).toUpperCase();
17942         //Roo.log(k);
17943         var match  = false;
17944         var csel = this.view.getSelectedNodes();
17945         var cselitem = false;
17946         if (csel.length) {
17947             var ix = this.view.indexOf(csel[0]);
17948             cselitem  = this.store.getAt(ix);
17949             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17950                 cselitem = false;
17951             }
17952             
17953         }
17954         
17955         this.store.each(function(v) { 
17956             if (cselitem) {
17957                 // start at existing selection.
17958                 if (cselitem.id == v.id) {
17959                     cselitem = false;
17960                 }
17961                 return true;
17962             }
17963                 
17964             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17965                 match = this.store.indexOf(v);
17966                 return false;
17967             }
17968             return true;
17969         }, this);
17970         
17971         if (match === false) {
17972             return true; // no more action?
17973         }
17974         // scroll to?
17975         this.view.select(match);
17976         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17977         sn.scrollIntoView(sn.dom.parentNode, false);
17978     },
17979     
17980     onViewScroll : function(e, t){
17981         
17982         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){
17983             return;
17984         }
17985         
17986         this.hasQuery = true;
17987         
17988         this.loading = this.list.select('.loading', true).first();
17989         
17990         if(this.loading === null){
17991             this.list.createChild({
17992                 tag: 'div',
17993                 cls: 'loading roo-select2-more-results roo-select2-active',
17994                 html: 'Loading more results...'
17995             });
17996             
17997             this.loading = this.list.select('.loading', true).first();
17998             
17999             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18000             
18001             this.loading.hide();
18002         }
18003         
18004         this.loading.show();
18005         
18006         var _combo = this;
18007         
18008         this.page++;
18009         this.loadNext = true;
18010         
18011         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18012         
18013         return;
18014     },
18015     
18016     addItem : function(o)
18017     {   
18018         var dv = ''; // display value
18019         
18020         if (this.displayField) {
18021             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18022         } else {
18023             // this is an error condition!!!
18024             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18025         }
18026         
18027         if(!dv.length){
18028             return;
18029         }
18030         
18031         var choice = this.choices.createChild({
18032             tag: 'li',
18033             cls: 'roo-select2-search-choice',
18034             cn: [
18035                 {
18036                     tag: 'div',
18037                     html: dv
18038                 },
18039                 {
18040                     tag: 'a',
18041                     href: '#',
18042                     cls: 'roo-select2-search-choice-close fa fa-times',
18043                     tabindex: '-1'
18044                 }
18045             ]
18046             
18047         }, this.searchField);
18048         
18049         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18050         
18051         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18052         
18053         this.item.push(o);
18054         
18055         this.lastData = o;
18056         
18057         this.syncValue();
18058         
18059         this.inputEl().dom.value = '';
18060         
18061         this.validate();
18062     },
18063     
18064     onRemoveItem : function(e, _self, o)
18065     {
18066         e.preventDefault();
18067         
18068         this.lastItem = Roo.apply([], this.item);
18069         
18070         var index = this.item.indexOf(o.data) * 1;
18071         
18072         if( index < 0){
18073             Roo.log('not this item?!');
18074             return;
18075         }
18076         
18077         this.item.splice(index, 1);
18078         o.item.remove();
18079         
18080         this.syncValue();
18081         
18082         this.fireEvent('remove', this, e);
18083         
18084         this.validate();
18085         
18086     },
18087     
18088     syncValue : function()
18089     {
18090         if(!this.item.length){
18091             this.clearValue();
18092             return;
18093         }
18094             
18095         var value = [];
18096         var _this = this;
18097         Roo.each(this.item, function(i){
18098             if(_this.valueField){
18099                 value.push(i[_this.valueField]);
18100                 return;
18101             }
18102
18103             value.push(i);
18104         });
18105
18106         this.value = value.join(',');
18107
18108         if(this.hiddenField){
18109             this.hiddenField.dom.value = this.value;
18110         }
18111         
18112         this.store.fireEvent("datachanged", this.store);
18113         
18114         this.validate();
18115     },
18116     
18117     clearItem : function()
18118     {
18119         if(!this.multiple){
18120             return;
18121         }
18122         
18123         this.item = [];
18124         
18125         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18126            c.remove();
18127         });
18128         
18129         this.syncValue();
18130         
18131         this.validate();
18132         
18133         if(this.tickable && !Roo.isTouch){
18134             this.view.refresh();
18135         }
18136     },
18137     
18138     inputEl: function ()
18139     {
18140         if(Roo.isIOS && this.useNativeIOS){
18141             return this.el.select('select.roo-ios-select', true).first();
18142         }
18143         
18144         if(Roo.isTouch && this.mobileTouchView){
18145             return this.el.select('input.form-control',true).first();
18146         }
18147         
18148         if(this.tickable){
18149             return this.searchField;
18150         }
18151         
18152         return this.el.select('input.form-control',true).first();
18153     },
18154     
18155     onTickableFooterButtonClick : function(e, btn, el)
18156     {
18157         e.preventDefault();
18158         
18159         this.lastItem = Roo.apply([], this.item);
18160         
18161         if(btn && btn.name == 'cancel'){
18162             this.tickItems = Roo.apply([], this.item);
18163             this.collapse();
18164             return;
18165         }
18166         
18167         this.clearItem();
18168         
18169         var _this = this;
18170         
18171         Roo.each(this.tickItems, function(o){
18172             _this.addItem(o);
18173         });
18174         
18175         this.collapse();
18176         
18177     },
18178     
18179     validate : function()
18180     {
18181         if(this.getVisibilityEl().hasClass('hidden')){
18182             return true;
18183         }
18184         
18185         var v = this.getRawValue();
18186         
18187         if(this.multiple){
18188             v = this.getValue();
18189         }
18190         
18191         if(this.disabled || this.allowBlank || v.length){
18192             this.markValid();
18193             return true;
18194         }
18195         
18196         this.markInvalid();
18197         return false;
18198     },
18199     
18200     tickableInputEl : function()
18201     {
18202         if(!this.tickable || !this.editable){
18203             return this.inputEl();
18204         }
18205         
18206         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18207     },
18208     
18209     
18210     getAutoCreateTouchView : function()
18211     {
18212         var id = Roo.id();
18213         
18214         var cfg = {
18215             cls: 'form-group' //input-group
18216         };
18217         
18218         var input =  {
18219             tag: 'input',
18220             id : id,
18221             type : this.inputType,
18222             cls : 'form-control x-combo-noedit',
18223             autocomplete: 'new-password',
18224             placeholder : this.placeholder || '',
18225             readonly : true
18226         };
18227         
18228         if (this.name) {
18229             input.name = this.name;
18230         }
18231         
18232         if (this.size) {
18233             input.cls += ' input-' + this.size;
18234         }
18235         
18236         if (this.disabled) {
18237             input.disabled = true;
18238         }
18239         
18240         var inputblock = {
18241             cls : 'roo-combobox-wrap',
18242             cn : [
18243                 input
18244             ]
18245         };
18246         
18247         if(this.before){
18248             inputblock.cls += ' input-group';
18249             
18250             inputblock.cn.unshift({
18251                 tag :'span',
18252                 cls : 'input-group-addon input-group-prepend input-group-text',
18253                 html : this.before
18254             });
18255         }
18256         
18257         if(this.removable && !this.multiple){
18258             inputblock.cls += ' roo-removable';
18259             
18260             inputblock.cn.push({
18261                 tag: 'button',
18262                 html : 'x',
18263                 cls : 'roo-combo-removable-btn close'
18264             });
18265         }
18266
18267         if(this.hasFeedback && !this.allowBlank){
18268             
18269             inputblock.cls += ' has-feedback';
18270             
18271             inputblock.cn.push({
18272                 tag: 'span',
18273                 cls: 'glyphicon form-control-feedback'
18274             });
18275             
18276         }
18277         
18278         if (this.after) {
18279             
18280             inputblock.cls += (this.before) ? '' : ' input-group';
18281             
18282             inputblock.cn.push({
18283                 tag :'span',
18284                 cls : 'input-group-addon input-group-append input-group-text',
18285                 html : this.after
18286             });
18287         }
18288
18289         
18290         var ibwrap = inputblock;
18291         
18292         if(this.multiple){
18293             ibwrap = {
18294                 tag: 'ul',
18295                 cls: 'roo-select2-choices',
18296                 cn:[
18297                     {
18298                         tag: 'li',
18299                         cls: 'roo-select2-search-field',
18300                         cn: [
18301
18302                             inputblock
18303                         ]
18304                     }
18305                 ]
18306             };
18307         
18308             
18309         }
18310         
18311         var combobox = {
18312             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18313             cn: [
18314                 {
18315                     tag: 'input',
18316                     type : 'hidden',
18317                     cls: 'form-hidden-field'
18318                 },
18319                 ibwrap
18320             ]
18321         };
18322         
18323         if(!this.multiple && this.showToggleBtn){
18324             
18325             var caret = {
18326                 cls: 'caret'
18327             };
18328             
18329             if (this.caret != false) {
18330                 caret = {
18331                      tag: 'i',
18332                      cls: 'fa fa-' + this.caret
18333                 };
18334                 
18335             }
18336             
18337             combobox.cn.push({
18338                 tag :'span',
18339                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18340                 cn : [
18341                     Roo.bootstrap.version == 3 ? caret : '',
18342                     {
18343                         tag: 'span',
18344                         cls: 'combobox-clear',
18345                         cn  : [
18346                             {
18347                                 tag : 'i',
18348                                 cls: 'icon-remove'
18349                             }
18350                         ]
18351                     }
18352                 ]
18353
18354             })
18355         }
18356         
18357         if(this.multiple){
18358             combobox.cls += ' roo-select2-container-multi';
18359         }
18360         
18361         var required =  this.allowBlank ?  {
18362                     tag : 'i',
18363                     style: 'display: none'
18364                 } : {
18365                    tag : 'i',
18366                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18367                    tooltip : 'This field is required'
18368                 };
18369         
18370         var align = this.labelAlign || this.parentLabelAlign();
18371         
18372         if (align ==='left' && this.fieldLabel.length) {
18373
18374             cfg.cn = [
18375                 required,
18376                 {
18377                     tag: 'label',
18378                     cls : 'control-label col-form-label',
18379                     html : this.fieldLabel
18380
18381                 },
18382                 {
18383                     cls : 'roo-combobox-wrap ', 
18384                     cn: [
18385                         combobox
18386                     ]
18387                 }
18388             ];
18389             
18390             var labelCfg = cfg.cn[1];
18391             var contentCfg = cfg.cn[2];
18392             
18393
18394             if(this.indicatorpos == 'right'){
18395                 cfg.cn = [
18396                     {
18397                         tag: 'label',
18398                         'for' :  id,
18399                         cls : 'control-label col-form-label',
18400                         cn : [
18401                             {
18402                                 tag : 'span',
18403                                 html : this.fieldLabel
18404                             },
18405                             required
18406                         ]
18407                     },
18408                     {
18409                         cls : "roo-combobox-wrap ",
18410                         cn: [
18411                             combobox
18412                         ]
18413                     }
18414
18415                 ];
18416                 
18417                 labelCfg = cfg.cn[0];
18418                 contentCfg = cfg.cn[1];
18419             }
18420             
18421            
18422             
18423             if(this.labelWidth > 12){
18424                 labelCfg.style = "width: " + this.labelWidth + 'px';
18425             }
18426            
18427             if(this.labelWidth < 13 && this.labelmd == 0){
18428                 this.labelmd = this.labelWidth;
18429             }
18430             
18431             if(this.labellg > 0){
18432                 labelCfg.cls += ' col-lg-' + this.labellg;
18433                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18434             }
18435             
18436             if(this.labelmd > 0){
18437                 labelCfg.cls += ' col-md-' + this.labelmd;
18438                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18439             }
18440             
18441             if(this.labelsm > 0){
18442                 labelCfg.cls += ' col-sm-' + this.labelsm;
18443                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18444             }
18445             
18446             if(this.labelxs > 0){
18447                 labelCfg.cls += ' col-xs-' + this.labelxs;
18448                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18449             }
18450                 
18451                 
18452         } else if ( this.fieldLabel.length) {
18453             cfg.cn = [
18454                required,
18455                 {
18456                     tag: 'label',
18457                     cls : 'control-label',
18458                     html : this.fieldLabel
18459
18460                 },
18461                 {
18462                     cls : '', 
18463                     cn: [
18464                         combobox
18465                     ]
18466                 }
18467             ];
18468             
18469             if(this.indicatorpos == 'right'){
18470                 cfg.cn = [
18471                     {
18472                         tag: 'label',
18473                         cls : 'control-label',
18474                         html : this.fieldLabel,
18475                         cn : [
18476                             required
18477                         ]
18478                     },
18479                     {
18480                         cls : '', 
18481                         cn: [
18482                             combobox
18483                         ]
18484                     }
18485                 ];
18486             }
18487         } else {
18488             cfg.cn = combobox;    
18489         }
18490         
18491         
18492         var settings = this;
18493         
18494         ['xs','sm','md','lg'].map(function(size){
18495             if (settings[size]) {
18496                 cfg.cls += ' col-' + size + '-' + settings[size];
18497             }
18498         });
18499         
18500         return cfg;
18501     },
18502     
18503     initTouchView : function()
18504     {
18505         this.renderTouchView();
18506         
18507         this.touchViewEl.on('scroll', function(){
18508             this.el.dom.scrollTop = 0;
18509         }, this);
18510         
18511         this.originalValue = this.getValue();
18512         
18513         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18514         
18515         this.inputEl().on("click", this.showTouchView, this);
18516         if (this.triggerEl) {
18517             this.triggerEl.on("click", this.showTouchView, this);
18518         }
18519         
18520         
18521         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18522         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18523         
18524         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18525         
18526         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18527         this.store.on('load', this.onTouchViewLoad, this);
18528         this.store.on('loadexception', this.onTouchViewLoadException, this);
18529         
18530         if(this.hiddenName){
18531             
18532             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18533             
18534             this.hiddenField.dom.value =
18535                 this.hiddenValue !== undefined ? this.hiddenValue :
18536                 this.value !== undefined ? this.value : '';
18537         
18538             this.el.dom.removeAttribute('name');
18539             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18540         }
18541         
18542         if(this.multiple){
18543             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18544             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18545         }
18546         
18547         if(this.removable && !this.multiple){
18548             var close = this.closeTriggerEl();
18549             if(close){
18550                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18551                 close.on('click', this.removeBtnClick, this, close);
18552             }
18553         }
18554         /*
18555          * fix the bug in Safari iOS8
18556          */
18557         this.inputEl().on("focus", function(e){
18558             document.activeElement.blur();
18559         }, this);
18560         
18561         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18562         
18563         return;
18564         
18565         
18566     },
18567     
18568     renderTouchView : function()
18569     {
18570         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18571         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18572         
18573         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18574         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18575         
18576         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18577         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18578         this.touchViewBodyEl.setStyle('overflow', 'auto');
18579         
18580         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18581         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18582         
18583         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18584         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18585         
18586     },
18587     
18588     showTouchView : function()
18589     {
18590         if(this.disabled){
18591             return;
18592         }
18593         
18594         this.touchViewHeaderEl.hide();
18595
18596         if(this.modalTitle.length){
18597             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18598             this.touchViewHeaderEl.show();
18599         }
18600
18601         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18602         this.touchViewEl.show();
18603
18604         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18605         
18606         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18607         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18608
18609         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18610
18611         if(this.modalTitle.length){
18612             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18613         }
18614         
18615         this.touchViewBodyEl.setHeight(bodyHeight);
18616
18617         if(this.animate){
18618             var _this = this;
18619             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18620         }else{
18621             this.touchViewEl.addClass(['in','show']);
18622         }
18623         
18624         if(this._touchViewMask){
18625             Roo.get(document.body).addClass("x-body-masked");
18626             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18627             this._touchViewMask.setStyle('z-index', 10000);
18628             this._touchViewMask.addClass('show');
18629         }
18630         
18631         this.doTouchViewQuery();
18632         
18633     },
18634     
18635     hideTouchView : function()
18636     {
18637         this.touchViewEl.removeClass(['in','show']);
18638
18639         if(this.animate){
18640             var _this = this;
18641             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18642         }else{
18643             this.touchViewEl.setStyle('display', 'none');
18644         }
18645         
18646         if(this._touchViewMask){
18647             this._touchViewMask.removeClass('show');
18648             Roo.get(document.body).removeClass("x-body-masked");
18649         }
18650     },
18651     
18652     setTouchViewValue : function()
18653     {
18654         if(this.multiple){
18655             this.clearItem();
18656         
18657             var _this = this;
18658
18659             Roo.each(this.tickItems, function(o){
18660                 this.addItem(o);
18661             }, this);
18662         }
18663         
18664         this.hideTouchView();
18665     },
18666     
18667     doTouchViewQuery : function()
18668     {
18669         var qe = {
18670             query: '',
18671             forceAll: true,
18672             combo: this,
18673             cancel:false
18674         };
18675         
18676         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18677             return false;
18678         }
18679         
18680         if(!this.alwaysQuery || this.mode == 'local'){
18681             this.onTouchViewLoad();
18682             return;
18683         }
18684         
18685         this.store.load();
18686     },
18687     
18688     onTouchViewBeforeLoad : function(combo,opts)
18689     {
18690         return;
18691     },
18692
18693     // private
18694     onTouchViewLoad : function()
18695     {
18696         if(this.store.getCount() < 1){
18697             this.onTouchViewEmptyResults();
18698             return;
18699         }
18700         
18701         this.clearTouchView();
18702         
18703         var rawValue = this.getRawValue();
18704         
18705         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18706         
18707         this.tickItems = [];
18708         
18709         this.store.data.each(function(d, rowIndex){
18710             var row = this.touchViewListGroup.createChild(template);
18711             
18712             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18713                 row.addClass(d.data.cls);
18714             }
18715             
18716             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18717                 var cfg = {
18718                     data : d.data,
18719                     html : d.data[this.displayField]
18720                 };
18721                 
18722                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18723                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18724                 }
18725             }
18726             row.removeClass('selected');
18727             if(!this.multiple && this.valueField &&
18728                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18729             {
18730                 // radio buttons..
18731                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18732                 row.addClass('selected');
18733             }
18734             
18735             if(this.multiple && this.valueField &&
18736                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18737             {
18738                 
18739                 // checkboxes...
18740                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18741                 this.tickItems.push(d.data);
18742             }
18743             
18744             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18745             
18746         }, this);
18747         
18748         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18749         
18750         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18751
18752         if(this.modalTitle.length){
18753             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18754         }
18755
18756         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18757         
18758         if(this.mobile_restrict_height && listHeight < bodyHeight){
18759             this.touchViewBodyEl.setHeight(listHeight);
18760         }
18761         
18762         var _this = this;
18763         
18764         if(firstChecked && listHeight > bodyHeight){
18765             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18766         }
18767         
18768     },
18769     
18770     onTouchViewLoadException : function()
18771     {
18772         this.hideTouchView();
18773     },
18774     
18775     onTouchViewEmptyResults : function()
18776     {
18777         this.clearTouchView();
18778         
18779         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18780         
18781         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18782         
18783     },
18784     
18785     clearTouchView : function()
18786     {
18787         this.touchViewListGroup.dom.innerHTML = '';
18788     },
18789     
18790     onTouchViewClick : function(e, el, o)
18791     {
18792         e.preventDefault();
18793         
18794         var row = o.row;
18795         var rowIndex = o.rowIndex;
18796         
18797         var r = this.store.getAt(rowIndex);
18798         
18799         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18800             
18801             if(!this.multiple){
18802                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18803                     c.dom.removeAttribute('checked');
18804                 }, this);
18805
18806                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18807
18808                 this.setFromData(r.data);
18809
18810                 var close = this.closeTriggerEl();
18811
18812                 if(close){
18813                     close.show();
18814                 }
18815
18816                 this.hideTouchView();
18817
18818                 this.fireEvent('select', this, r, rowIndex);
18819
18820                 return;
18821             }
18822
18823             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18824                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18825                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18826                 return;
18827             }
18828
18829             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18830             this.addItem(r.data);
18831             this.tickItems.push(r.data);
18832         }
18833     },
18834     
18835     getAutoCreateNativeIOS : function()
18836     {
18837         var cfg = {
18838             cls: 'form-group' //input-group,
18839         };
18840         
18841         var combobox =  {
18842             tag: 'select',
18843             cls : 'roo-ios-select'
18844         };
18845         
18846         if (this.name) {
18847             combobox.name = this.name;
18848         }
18849         
18850         if (this.disabled) {
18851             combobox.disabled = true;
18852         }
18853         
18854         var settings = this;
18855         
18856         ['xs','sm','md','lg'].map(function(size){
18857             if (settings[size]) {
18858                 cfg.cls += ' col-' + size + '-' + settings[size];
18859             }
18860         });
18861         
18862         cfg.cn = combobox;
18863         
18864         return cfg;
18865         
18866     },
18867     
18868     initIOSView : function()
18869     {
18870         this.store.on('load', this.onIOSViewLoad, this);
18871         
18872         return;
18873     },
18874     
18875     onIOSViewLoad : function()
18876     {
18877         if(this.store.getCount() < 1){
18878             return;
18879         }
18880         
18881         this.clearIOSView();
18882         
18883         if(this.allowBlank) {
18884             
18885             var default_text = '-- SELECT --';
18886             
18887             if(this.placeholder.length){
18888                 default_text = this.placeholder;
18889             }
18890             
18891             if(this.emptyTitle.length){
18892                 default_text += ' - ' + this.emptyTitle + ' -';
18893             }
18894             
18895             var opt = this.inputEl().createChild({
18896                 tag: 'option',
18897                 value : 0,
18898                 html : default_text
18899             });
18900             
18901             var o = {};
18902             o[this.valueField] = 0;
18903             o[this.displayField] = default_text;
18904             
18905             this.ios_options.push({
18906                 data : o,
18907                 el : opt
18908             });
18909             
18910         }
18911         
18912         this.store.data.each(function(d, rowIndex){
18913             
18914             var html = '';
18915             
18916             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18917                 html = d.data[this.displayField];
18918             }
18919             
18920             var value = '';
18921             
18922             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18923                 value = d.data[this.valueField];
18924             }
18925             
18926             var option = {
18927                 tag: 'option',
18928                 value : value,
18929                 html : html
18930             };
18931             
18932             if(this.value == d.data[this.valueField]){
18933                 option['selected'] = true;
18934             }
18935             
18936             var opt = this.inputEl().createChild(option);
18937             
18938             this.ios_options.push({
18939                 data : d.data,
18940                 el : opt
18941             });
18942             
18943         }, this);
18944         
18945         this.inputEl().on('change', function(){
18946            this.fireEvent('select', this);
18947         }, this);
18948         
18949     },
18950     
18951     clearIOSView: function()
18952     {
18953         this.inputEl().dom.innerHTML = '';
18954         
18955         this.ios_options = [];
18956     },
18957     
18958     setIOSValue: function(v)
18959     {
18960         this.value = v;
18961         
18962         if(!this.ios_options){
18963             return;
18964         }
18965         
18966         Roo.each(this.ios_options, function(opts){
18967            
18968            opts.el.dom.removeAttribute('selected');
18969            
18970            if(opts.data[this.valueField] != v){
18971                return;
18972            }
18973            
18974            opts.el.dom.setAttribute('selected', true);
18975            
18976         }, this);
18977     }
18978
18979     /** 
18980     * @cfg {Boolean} grow 
18981     * @hide 
18982     */
18983     /** 
18984     * @cfg {Number} growMin 
18985     * @hide 
18986     */
18987     /** 
18988     * @cfg {Number} growMax 
18989     * @hide 
18990     */
18991     /**
18992      * @hide
18993      * @method autoSize
18994      */
18995 });
18996
18997 Roo.apply(Roo.bootstrap.ComboBox,  {
18998     
18999     header : {
19000         tag: 'div',
19001         cls: 'modal-header',
19002         cn: [
19003             {
19004                 tag: 'h4',
19005                 cls: 'modal-title'
19006             }
19007         ]
19008     },
19009     
19010     body : {
19011         tag: 'div',
19012         cls: 'modal-body',
19013         cn: [
19014             {
19015                 tag: 'ul',
19016                 cls: 'list-group'
19017             }
19018         ]
19019     },
19020     
19021     listItemRadio : {
19022         tag: 'li',
19023         cls: 'list-group-item',
19024         cn: [
19025             {
19026                 tag: 'span',
19027                 cls: 'roo-combobox-list-group-item-value'
19028             },
19029             {
19030                 tag: 'div',
19031                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19032                 cn: [
19033                     {
19034                         tag: 'input',
19035                         type: 'radio'
19036                     },
19037                     {
19038                         tag: 'label'
19039                     }
19040                 ]
19041             }
19042         ]
19043     },
19044     
19045     listItemCheckbox : {
19046         tag: 'li',
19047         cls: 'list-group-item',
19048         cn: [
19049             {
19050                 tag: 'span',
19051                 cls: 'roo-combobox-list-group-item-value'
19052             },
19053             {
19054                 tag: 'div',
19055                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19056                 cn: [
19057                     {
19058                         tag: 'input',
19059                         type: 'checkbox'
19060                     },
19061                     {
19062                         tag: 'label'
19063                     }
19064                 ]
19065             }
19066         ]
19067     },
19068     
19069     emptyResult : {
19070         tag: 'div',
19071         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19072     },
19073     
19074     footer : {
19075         tag: 'div',
19076         cls: 'modal-footer',
19077         cn: [
19078             {
19079                 tag: 'div',
19080                 cls: 'row',
19081                 cn: [
19082                     {
19083                         tag: 'div',
19084                         cls: 'col-xs-6 text-left',
19085                         cn: {
19086                             tag: 'button',
19087                             cls: 'btn btn-danger roo-touch-view-cancel',
19088                             html: 'Cancel'
19089                         }
19090                     },
19091                     {
19092                         tag: 'div',
19093                         cls: 'col-xs-6 text-right',
19094                         cn: {
19095                             tag: 'button',
19096                             cls: 'btn btn-success roo-touch-view-ok',
19097                             html: 'OK'
19098                         }
19099                     }
19100                 ]
19101             }
19102         ]
19103         
19104     }
19105 });
19106
19107 Roo.apply(Roo.bootstrap.ComboBox,  {
19108     
19109     touchViewTemplate : {
19110         tag: 'div',
19111         cls: 'modal fade roo-combobox-touch-view',
19112         cn: [
19113             {
19114                 tag: 'div',
19115                 cls: 'modal-dialog',
19116                 style : 'position:fixed', // we have to fix position....
19117                 cn: [
19118                     {
19119                         tag: 'div',
19120                         cls: 'modal-content',
19121                         cn: [
19122                             Roo.bootstrap.ComboBox.header,
19123                             Roo.bootstrap.ComboBox.body,
19124                             Roo.bootstrap.ComboBox.footer
19125                         ]
19126                     }
19127                 ]
19128             }
19129         ]
19130     }
19131 });/*
19132  * Based on:
19133  * Ext JS Library 1.1.1
19134  * Copyright(c) 2006-2007, Ext JS, LLC.
19135  *
19136  * Originally Released Under LGPL - original licence link has changed is not relivant.
19137  *
19138  * Fork - LGPL
19139  * <script type="text/javascript">
19140  */
19141
19142 /**
19143  * @class Roo.View
19144  * @extends Roo.util.Observable
19145  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19146  * This class also supports single and multi selection modes. <br>
19147  * Create a data model bound view:
19148  <pre><code>
19149  var store = new Roo.data.Store(...);
19150
19151  var view = new Roo.View({
19152     el : "my-element",
19153     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19154  
19155     singleSelect: true,
19156     selectedClass: "ydataview-selected",
19157     store: store
19158  });
19159
19160  // listen for node click?
19161  view.on("click", function(vw, index, node, e){
19162  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19163  });
19164
19165  // load XML data
19166  dataModel.load("foobar.xml");
19167  </code></pre>
19168  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19169  * <br><br>
19170  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19171  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19172  * 
19173  * Note: old style constructor is still suported (container, template, config)
19174  * 
19175  * @constructor
19176  * Create a new View
19177  * @param {Object} config The config object
19178  * 
19179  */
19180 Roo.View = function(config, depreciated_tpl, depreciated_config){
19181     
19182     this.parent = false;
19183     
19184     if (typeof(depreciated_tpl) == 'undefined') {
19185         // new way.. - universal constructor.
19186         Roo.apply(this, config);
19187         this.el  = Roo.get(this.el);
19188     } else {
19189         // old format..
19190         this.el  = Roo.get(config);
19191         this.tpl = depreciated_tpl;
19192         Roo.apply(this, depreciated_config);
19193     }
19194     this.wrapEl  = this.el.wrap().wrap();
19195     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19196     
19197     
19198     if(typeof(this.tpl) == "string"){
19199         this.tpl = new Roo.Template(this.tpl);
19200     } else {
19201         // support xtype ctors..
19202         this.tpl = new Roo.factory(this.tpl, Roo);
19203     }
19204     
19205     
19206     this.tpl.compile();
19207     
19208     /** @private */
19209     this.addEvents({
19210         /**
19211          * @event beforeclick
19212          * Fires before a click is processed. Returns false to cancel the default action.
19213          * @param {Roo.View} this
19214          * @param {Number} index The index of the target node
19215          * @param {HTMLElement} node The target node
19216          * @param {Roo.EventObject} e The raw event object
19217          */
19218             "beforeclick" : true,
19219         /**
19220          * @event click
19221          * Fires when a template node is clicked.
19222          * @param {Roo.View} this
19223          * @param {Number} index The index of the target node
19224          * @param {HTMLElement} node The target node
19225          * @param {Roo.EventObject} e The raw event object
19226          */
19227             "click" : true,
19228         /**
19229          * @event dblclick
19230          * Fires when a template node is double clicked.
19231          * @param {Roo.View} this
19232          * @param {Number} index The index of the target node
19233          * @param {HTMLElement} node The target node
19234          * @param {Roo.EventObject} e The raw event object
19235          */
19236             "dblclick" : true,
19237         /**
19238          * @event contextmenu
19239          * Fires when a template node is right clicked.
19240          * @param {Roo.View} this
19241          * @param {Number} index The index of the target node
19242          * @param {HTMLElement} node The target node
19243          * @param {Roo.EventObject} e The raw event object
19244          */
19245             "contextmenu" : true,
19246         /**
19247          * @event selectionchange
19248          * Fires when the selected nodes change.
19249          * @param {Roo.View} this
19250          * @param {Array} selections Array of the selected nodes
19251          */
19252             "selectionchange" : true,
19253     
19254         /**
19255          * @event beforeselect
19256          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19257          * @param {Roo.View} this
19258          * @param {HTMLElement} node The node to be selected
19259          * @param {Array} selections Array of currently selected nodes
19260          */
19261             "beforeselect" : true,
19262         /**
19263          * @event preparedata
19264          * Fires on every row to render, to allow you to change the data.
19265          * @param {Roo.View} this
19266          * @param {Object} data to be rendered (change this)
19267          */
19268           "preparedata" : true
19269           
19270           
19271         });
19272
19273
19274
19275     this.el.on({
19276         "click": this.onClick,
19277         "dblclick": this.onDblClick,
19278         "contextmenu": this.onContextMenu,
19279         scope:this
19280     });
19281
19282     this.selections = [];
19283     this.nodes = [];
19284     this.cmp = new Roo.CompositeElementLite([]);
19285     if(this.store){
19286         this.store = Roo.factory(this.store, Roo.data);
19287         this.setStore(this.store, true);
19288     }
19289     
19290     if ( this.footer && this.footer.xtype) {
19291            
19292          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19293         
19294         this.footer.dataSource = this.store;
19295         this.footer.container = fctr;
19296         this.footer = Roo.factory(this.footer, Roo);
19297         fctr.insertFirst(this.el);
19298         
19299         // this is a bit insane - as the paging toolbar seems to detach the el..
19300 //        dom.parentNode.parentNode.parentNode
19301          // they get detached?
19302     }
19303     
19304     
19305     Roo.View.superclass.constructor.call(this);
19306     
19307     
19308 };
19309
19310 Roo.extend(Roo.View, Roo.util.Observable, {
19311     
19312      /**
19313      * @cfg {Roo.data.Store} store Data store to load data from.
19314      */
19315     store : false,
19316     
19317     /**
19318      * @cfg {String|Roo.Element} el The container element.
19319      */
19320     el : '',
19321     
19322     /**
19323      * @cfg {String|Roo.Template} tpl The template used by this View 
19324      */
19325     tpl : false,
19326     /**
19327      * @cfg {String} dataName the named area of the template to use as the data area
19328      *                          Works with domtemplates roo-name="name"
19329      */
19330     dataName: false,
19331     /**
19332      * @cfg {String} selectedClass The css class to add to selected nodes
19333      */
19334     selectedClass : "x-view-selected",
19335      /**
19336      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19337      */
19338     emptyText : "",
19339     
19340     /**
19341      * @cfg {String} text to display on mask (default Loading)
19342      */
19343     mask : false,
19344     /**
19345      * @cfg {Boolean} multiSelect Allow multiple selection
19346      */
19347     multiSelect : false,
19348     /**
19349      * @cfg {Boolean} singleSelect Allow single selection
19350      */
19351     singleSelect:  false,
19352     
19353     /**
19354      * @cfg {Boolean} toggleSelect - selecting 
19355      */
19356     toggleSelect : false,
19357     
19358     /**
19359      * @cfg {Boolean} tickable - selecting 
19360      */
19361     tickable : false,
19362     
19363     /**
19364      * Returns the element this view is bound to.
19365      * @return {Roo.Element}
19366      */
19367     getEl : function(){
19368         return this.wrapEl;
19369     },
19370     
19371     
19372
19373     /**
19374      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19375      */
19376     refresh : function(){
19377         //Roo.log('refresh');
19378         var t = this.tpl;
19379         
19380         // if we are using something like 'domtemplate', then
19381         // the what gets used is:
19382         // t.applySubtemplate(NAME, data, wrapping data..)
19383         // the outer template then get' applied with
19384         //     the store 'extra data'
19385         // and the body get's added to the
19386         //      roo-name="data" node?
19387         //      <span class='roo-tpl-{name}'></span> ?????
19388         
19389         
19390         
19391         this.clearSelections();
19392         this.el.update("");
19393         var html = [];
19394         var records = this.store.getRange();
19395         if(records.length < 1) {
19396             
19397             // is this valid??  = should it render a template??
19398             
19399             this.el.update(this.emptyText);
19400             return;
19401         }
19402         var el = this.el;
19403         if (this.dataName) {
19404             this.el.update(t.apply(this.store.meta)); //????
19405             el = this.el.child('.roo-tpl-' + this.dataName);
19406         }
19407         
19408         for(var i = 0, len = records.length; i < len; i++){
19409             var data = this.prepareData(records[i].data, i, records[i]);
19410             this.fireEvent("preparedata", this, data, i, records[i]);
19411             
19412             var d = Roo.apply({}, data);
19413             
19414             if(this.tickable){
19415                 Roo.apply(d, {'roo-id' : Roo.id()});
19416                 
19417                 var _this = this;
19418             
19419                 Roo.each(this.parent.item, function(item){
19420                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19421                         return;
19422                     }
19423                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19424                 });
19425             }
19426             
19427             html[html.length] = Roo.util.Format.trim(
19428                 this.dataName ?
19429                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19430                     t.apply(d)
19431             );
19432         }
19433         
19434         
19435         
19436         el.update(html.join(""));
19437         this.nodes = el.dom.childNodes;
19438         this.updateIndexes(0);
19439     },
19440     
19441
19442     /**
19443      * Function to override to reformat the data that is sent to
19444      * the template for each node.
19445      * DEPRICATED - use the preparedata event handler.
19446      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19447      * a JSON object for an UpdateManager bound view).
19448      */
19449     prepareData : function(data, index, record)
19450     {
19451         this.fireEvent("preparedata", this, data, index, record);
19452         return data;
19453     },
19454
19455     onUpdate : function(ds, record){
19456         // Roo.log('on update');   
19457         this.clearSelections();
19458         var index = this.store.indexOf(record);
19459         var n = this.nodes[index];
19460         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19461         n.parentNode.removeChild(n);
19462         this.updateIndexes(index, index);
19463     },
19464
19465     
19466     
19467 // --------- FIXME     
19468     onAdd : function(ds, records, index)
19469     {
19470         //Roo.log(['on Add', ds, records, index] );        
19471         this.clearSelections();
19472         if(this.nodes.length == 0){
19473             this.refresh();
19474             return;
19475         }
19476         var n = this.nodes[index];
19477         for(var i = 0, len = records.length; i < len; i++){
19478             var d = this.prepareData(records[i].data, i, records[i]);
19479             if(n){
19480                 this.tpl.insertBefore(n, d);
19481             }else{
19482                 
19483                 this.tpl.append(this.el, d);
19484             }
19485         }
19486         this.updateIndexes(index);
19487     },
19488
19489     onRemove : function(ds, record, index){
19490        // Roo.log('onRemove');
19491         this.clearSelections();
19492         var el = this.dataName  ?
19493             this.el.child('.roo-tpl-' + this.dataName) :
19494             this.el; 
19495         
19496         el.dom.removeChild(this.nodes[index]);
19497         this.updateIndexes(index);
19498     },
19499
19500     /**
19501      * Refresh an individual node.
19502      * @param {Number} index
19503      */
19504     refreshNode : function(index){
19505         this.onUpdate(this.store, this.store.getAt(index));
19506     },
19507
19508     updateIndexes : function(startIndex, endIndex){
19509         var ns = this.nodes;
19510         startIndex = startIndex || 0;
19511         endIndex = endIndex || ns.length - 1;
19512         for(var i = startIndex; i <= endIndex; i++){
19513             ns[i].nodeIndex = i;
19514         }
19515     },
19516
19517     /**
19518      * Changes the data store this view uses and refresh the view.
19519      * @param {Store} store
19520      */
19521     setStore : function(store, initial){
19522         if(!initial && this.store){
19523             this.store.un("datachanged", this.refresh);
19524             this.store.un("add", this.onAdd);
19525             this.store.un("remove", this.onRemove);
19526             this.store.un("update", this.onUpdate);
19527             this.store.un("clear", this.refresh);
19528             this.store.un("beforeload", this.onBeforeLoad);
19529             this.store.un("load", this.onLoad);
19530             this.store.un("loadexception", this.onLoad);
19531         }
19532         if(store){
19533           
19534             store.on("datachanged", this.refresh, this);
19535             store.on("add", this.onAdd, this);
19536             store.on("remove", this.onRemove, this);
19537             store.on("update", this.onUpdate, this);
19538             store.on("clear", this.refresh, this);
19539             store.on("beforeload", this.onBeforeLoad, this);
19540             store.on("load", this.onLoad, this);
19541             store.on("loadexception", this.onLoad, this);
19542         }
19543         
19544         if(store){
19545             this.refresh();
19546         }
19547     },
19548     /**
19549      * onbeforeLoad - masks the loading area.
19550      *
19551      */
19552     onBeforeLoad : function(store,opts)
19553     {
19554          //Roo.log('onBeforeLoad');   
19555         if (!opts.add) {
19556             this.el.update("");
19557         }
19558         this.el.mask(this.mask ? this.mask : "Loading" ); 
19559     },
19560     onLoad : function ()
19561     {
19562         this.el.unmask();
19563     },
19564     
19565
19566     /**
19567      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19568      * @param {HTMLElement} node
19569      * @return {HTMLElement} The template node
19570      */
19571     findItemFromChild : function(node){
19572         var el = this.dataName  ?
19573             this.el.child('.roo-tpl-' + this.dataName,true) :
19574             this.el.dom; 
19575         
19576         if(!node || node.parentNode == el){
19577                     return node;
19578             }
19579             var p = node.parentNode;
19580             while(p && p != el){
19581             if(p.parentNode == el){
19582                 return p;
19583             }
19584             p = p.parentNode;
19585         }
19586             return null;
19587     },
19588
19589     /** @ignore */
19590     onClick : function(e){
19591         var item = this.findItemFromChild(e.getTarget());
19592         if(item){
19593             var index = this.indexOf(item);
19594             if(this.onItemClick(item, index, e) !== false){
19595                 this.fireEvent("click", this, index, item, e);
19596             }
19597         }else{
19598             this.clearSelections();
19599         }
19600     },
19601
19602     /** @ignore */
19603     onContextMenu : function(e){
19604         var item = this.findItemFromChild(e.getTarget());
19605         if(item){
19606             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19607         }
19608     },
19609
19610     /** @ignore */
19611     onDblClick : function(e){
19612         var item = this.findItemFromChild(e.getTarget());
19613         if(item){
19614             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19615         }
19616     },
19617
19618     onItemClick : function(item, index, e)
19619     {
19620         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19621             return false;
19622         }
19623         if (this.toggleSelect) {
19624             var m = this.isSelected(item) ? 'unselect' : 'select';
19625             //Roo.log(m);
19626             var _t = this;
19627             _t[m](item, true, false);
19628             return true;
19629         }
19630         if(this.multiSelect || this.singleSelect){
19631             if(this.multiSelect && e.shiftKey && this.lastSelection){
19632                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19633             }else{
19634                 this.select(item, this.multiSelect && e.ctrlKey);
19635                 this.lastSelection = item;
19636             }
19637             
19638             if(!this.tickable){
19639                 e.preventDefault();
19640             }
19641             
19642         }
19643         return true;
19644     },
19645
19646     /**
19647      * Get the number of selected nodes.
19648      * @return {Number}
19649      */
19650     getSelectionCount : function(){
19651         return this.selections.length;
19652     },
19653
19654     /**
19655      * Get the currently selected nodes.
19656      * @return {Array} An array of HTMLElements
19657      */
19658     getSelectedNodes : function(){
19659         return this.selections;
19660     },
19661
19662     /**
19663      * Get the indexes of the selected nodes.
19664      * @return {Array}
19665      */
19666     getSelectedIndexes : function(){
19667         var indexes = [], s = this.selections;
19668         for(var i = 0, len = s.length; i < len; i++){
19669             indexes.push(s[i].nodeIndex);
19670         }
19671         return indexes;
19672     },
19673
19674     /**
19675      * Clear all selections
19676      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19677      */
19678     clearSelections : function(suppressEvent){
19679         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19680             this.cmp.elements = this.selections;
19681             this.cmp.removeClass(this.selectedClass);
19682             this.selections = [];
19683             if(!suppressEvent){
19684                 this.fireEvent("selectionchange", this, this.selections);
19685             }
19686         }
19687     },
19688
19689     /**
19690      * Returns true if the passed node is selected
19691      * @param {HTMLElement/Number} node The node or node index
19692      * @return {Boolean}
19693      */
19694     isSelected : function(node){
19695         var s = this.selections;
19696         if(s.length < 1){
19697             return false;
19698         }
19699         node = this.getNode(node);
19700         return s.indexOf(node) !== -1;
19701     },
19702
19703     /**
19704      * Selects nodes.
19705      * @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
19706      * @param {Boolean} keepExisting (optional) true to keep existing selections
19707      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19708      */
19709     select : function(nodeInfo, keepExisting, suppressEvent){
19710         if(nodeInfo instanceof Array){
19711             if(!keepExisting){
19712                 this.clearSelections(true);
19713             }
19714             for(var i = 0, len = nodeInfo.length; i < len; i++){
19715                 this.select(nodeInfo[i], true, true);
19716             }
19717             return;
19718         } 
19719         var node = this.getNode(nodeInfo);
19720         if(!node || this.isSelected(node)){
19721             return; // already selected.
19722         }
19723         if(!keepExisting){
19724             this.clearSelections(true);
19725         }
19726         
19727         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19728             Roo.fly(node).addClass(this.selectedClass);
19729             this.selections.push(node);
19730             if(!suppressEvent){
19731                 this.fireEvent("selectionchange", this, this.selections);
19732             }
19733         }
19734         
19735         
19736     },
19737       /**
19738      * Unselects nodes.
19739      * @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
19740      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19741      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19742      */
19743     unselect : function(nodeInfo, keepExisting, suppressEvent)
19744     {
19745         if(nodeInfo instanceof Array){
19746             Roo.each(this.selections, function(s) {
19747                 this.unselect(s, nodeInfo);
19748             }, this);
19749             return;
19750         }
19751         var node = this.getNode(nodeInfo);
19752         if(!node || !this.isSelected(node)){
19753             //Roo.log("not selected");
19754             return; // not selected.
19755         }
19756         // fireevent???
19757         var ns = [];
19758         Roo.each(this.selections, function(s) {
19759             if (s == node ) {
19760                 Roo.fly(node).removeClass(this.selectedClass);
19761
19762                 return;
19763             }
19764             ns.push(s);
19765         },this);
19766         
19767         this.selections= ns;
19768         this.fireEvent("selectionchange", this, this.selections);
19769     },
19770
19771     /**
19772      * Gets a template node.
19773      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19774      * @return {HTMLElement} The node or null if it wasn't found
19775      */
19776     getNode : function(nodeInfo){
19777         if(typeof nodeInfo == "string"){
19778             return document.getElementById(nodeInfo);
19779         }else if(typeof nodeInfo == "number"){
19780             return this.nodes[nodeInfo];
19781         }
19782         return nodeInfo;
19783     },
19784
19785     /**
19786      * Gets a range template nodes.
19787      * @param {Number} startIndex
19788      * @param {Number} endIndex
19789      * @return {Array} An array of nodes
19790      */
19791     getNodes : function(start, end){
19792         var ns = this.nodes;
19793         start = start || 0;
19794         end = typeof end == "undefined" ? ns.length - 1 : end;
19795         var nodes = [];
19796         if(start <= end){
19797             for(var i = start; i <= end; i++){
19798                 nodes.push(ns[i]);
19799             }
19800         } else{
19801             for(var i = start; i >= end; i--){
19802                 nodes.push(ns[i]);
19803             }
19804         }
19805         return nodes;
19806     },
19807
19808     /**
19809      * Finds the index of the passed node
19810      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19811      * @return {Number} The index of the node or -1
19812      */
19813     indexOf : function(node){
19814         node = this.getNode(node);
19815         if(typeof node.nodeIndex == "number"){
19816             return node.nodeIndex;
19817         }
19818         var ns = this.nodes;
19819         for(var i = 0, len = ns.length; i < len; i++){
19820             if(ns[i] == node){
19821                 return i;
19822             }
19823         }
19824         return -1;
19825     }
19826 });
19827 /*
19828  * - LGPL
19829  *
19830  * based on jquery fullcalendar
19831  * 
19832  */
19833
19834 Roo.bootstrap = Roo.bootstrap || {};
19835 /**
19836  * @class Roo.bootstrap.Calendar
19837  * @extends Roo.bootstrap.Component
19838  * Bootstrap Calendar class
19839  * @cfg {Boolean} loadMask (true|false) default false
19840  * @cfg {Object} header generate the user specific header of the calendar, default false
19841
19842  * @constructor
19843  * Create a new Container
19844  * @param {Object} config The config object
19845  */
19846
19847
19848
19849 Roo.bootstrap.Calendar = function(config){
19850     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19851      this.addEvents({
19852         /**
19853              * @event select
19854              * Fires when a date is selected
19855              * @param {DatePicker} this
19856              * @param {Date} date The selected date
19857              */
19858         'select': true,
19859         /**
19860              * @event monthchange
19861              * Fires when the displayed month changes 
19862              * @param {DatePicker} this
19863              * @param {Date} date The selected month
19864              */
19865         'monthchange': true,
19866         /**
19867              * @event evententer
19868              * Fires when mouse over an event
19869              * @param {Calendar} this
19870              * @param {event} Event
19871              */
19872         'evententer': true,
19873         /**
19874              * @event eventleave
19875              * Fires when the mouse leaves an
19876              * @param {Calendar} this
19877              * @param {event}
19878              */
19879         'eventleave': true,
19880         /**
19881              * @event eventclick
19882              * Fires when the mouse click an
19883              * @param {Calendar} this
19884              * @param {event}
19885              */
19886         'eventclick': true
19887         
19888     });
19889
19890 };
19891
19892 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19893     
19894      /**
19895      * @cfg {Number} startDay
19896      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19897      */
19898     startDay : 0,
19899     
19900     loadMask : false,
19901     
19902     header : false,
19903       
19904     getAutoCreate : function(){
19905         
19906         
19907         var fc_button = function(name, corner, style, content ) {
19908             return Roo.apply({},{
19909                 tag : 'span',
19910                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19911                          (corner.length ?
19912                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19913                             ''
19914                         ),
19915                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19916                 unselectable: 'on'
19917             });
19918         };
19919         
19920         var header = {};
19921         
19922         if(!this.header){
19923             header = {
19924                 tag : 'table',
19925                 cls : 'fc-header',
19926                 style : 'width:100%',
19927                 cn : [
19928                     {
19929                         tag: 'tr',
19930                         cn : [
19931                             {
19932                                 tag : 'td',
19933                                 cls : 'fc-header-left',
19934                                 cn : [
19935                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19936                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19937                                     { tag: 'span', cls: 'fc-header-space' },
19938                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19939
19940
19941                                 ]
19942                             },
19943
19944                             {
19945                                 tag : 'td',
19946                                 cls : 'fc-header-center',
19947                                 cn : [
19948                                     {
19949                                         tag: 'span',
19950                                         cls: 'fc-header-title',
19951                                         cn : {
19952                                             tag: 'H2',
19953                                             html : 'month / year'
19954                                         }
19955                                     }
19956
19957                                 ]
19958                             },
19959                             {
19960                                 tag : 'td',
19961                                 cls : 'fc-header-right',
19962                                 cn : [
19963                               /*      fc_button('month', 'left', '', 'month' ),
19964                                     fc_button('week', '', '', 'week' ),
19965                                     fc_button('day', 'right', '', 'day' )
19966                                 */    
19967
19968                                 ]
19969                             }
19970
19971                         ]
19972                     }
19973                 ]
19974             };
19975         }
19976         
19977         header = this.header;
19978         
19979        
19980         var cal_heads = function() {
19981             var ret = [];
19982             // fixme - handle this.
19983             
19984             for (var i =0; i < Date.dayNames.length; i++) {
19985                 var d = Date.dayNames[i];
19986                 ret.push({
19987                     tag: 'th',
19988                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19989                     html : d.substring(0,3)
19990                 });
19991                 
19992             }
19993             ret[0].cls += ' fc-first';
19994             ret[6].cls += ' fc-last';
19995             return ret;
19996         };
19997         var cal_cell = function(n) {
19998             return  {
19999                 tag: 'td',
20000                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20001                 cn : [
20002                     {
20003                         cn : [
20004                             {
20005                                 cls: 'fc-day-number',
20006                                 html: 'D'
20007                             },
20008                             {
20009                                 cls: 'fc-day-content',
20010                              
20011                                 cn : [
20012                                      {
20013                                         style: 'position: relative;' // height: 17px;
20014                                     }
20015                                 ]
20016                             }
20017                             
20018                             
20019                         ]
20020                     }
20021                 ]
20022                 
20023             }
20024         };
20025         var cal_rows = function() {
20026             
20027             var ret = [];
20028             for (var r = 0; r < 6; r++) {
20029                 var row= {
20030                     tag : 'tr',
20031                     cls : 'fc-week',
20032                     cn : []
20033                 };
20034                 
20035                 for (var i =0; i < Date.dayNames.length; i++) {
20036                     var d = Date.dayNames[i];
20037                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20038
20039                 }
20040                 row.cn[0].cls+=' fc-first';
20041                 row.cn[0].cn[0].style = 'min-height:90px';
20042                 row.cn[6].cls+=' fc-last';
20043                 ret.push(row);
20044                 
20045             }
20046             ret[0].cls += ' fc-first';
20047             ret[4].cls += ' fc-prev-last';
20048             ret[5].cls += ' fc-last';
20049             return ret;
20050             
20051         };
20052         
20053         var cal_table = {
20054             tag: 'table',
20055             cls: 'fc-border-separate',
20056             style : 'width:100%',
20057             cellspacing  : 0,
20058             cn : [
20059                 { 
20060                     tag: 'thead',
20061                     cn : [
20062                         { 
20063                             tag: 'tr',
20064                             cls : 'fc-first fc-last',
20065                             cn : cal_heads()
20066                         }
20067                     ]
20068                 },
20069                 { 
20070                     tag: 'tbody',
20071                     cn : cal_rows()
20072                 }
20073                   
20074             ]
20075         };
20076          
20077          var cfg = {
20078             cls : 'fc fc-ltr',
20079             cn : [
20080                 header,
20081                 {
20082                     cls : 'fc-content',
20083                     style : "position: relative;",
20084                     cn : [
20085                         {
20086                             cls : 'fc-view fc-view-month fc-grid',
20087                             style : 'position: relative',
20088                             unselectable : 'on',
20089                             cn : [
20090                                 {
20091                                     cls : 'fc-event-container',
20092                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20093                                 },
20094                                 cal_table
20095                             ]
20096                         }
20097                     ]
20098     
20099                 }
20100            ] 
20101             
20102         };
20103         
20104          
20105         
20106         return cfg;
20107     },
20108     
20109     
20110     initEvents : function()
20111     {
20112         if(!this.store){
20113             throw "can not find store for calendar";
20114         }
20115         
20116         var mark = {
20117             tag: "div",
20118             cls:"x-dlg-mask",
20119             style: "text-align:center",
20120             cn: [
20121                 {
20122                     tag: "div",
20123                     style: "background-color:white;width:50%;margin:250 auto",
20124                     cn: [
20125                         {
20126                             tag: "img",
20127                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20128                         },
20129                         {
20130                             tag: "span",
20131                             html: "Loading"
20132                         }
20133                         
20134                     ]
20135                 }
20136             ]
20137         };
20138         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20139         
20140         var size = this.el.select('.fc-content', true).first().getSize();
20141         this.maskEl.setSize(size.width, size.height);
20142         this.maskEl.enableDisplayMode("block");
20143         if(!this.loadMask){
20144             this.maskEl.hide();
20145         }
20146         
20147         this.store = Roo.factory(this.store, Roo.data);
20148         this.store.on('load', this.onLoad, this);
20149         this.store.on('beforeload', this.onBeforeLoad, this);
20150         
20151         this.resize();
20152         
20153         this.cells = this.el.select('.fc-day',true);
20154         //Roo.log(this.cells);
20155         this.textNodes = this.el.query('.fc-day-number');
20156         this.cells.addClassOnOver('fc-state-hover');
20157         
20158         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20159         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20160         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20161         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20162         
20163         this.on('monthchange', this.onMonthChange, this);
20164         
20165         this.update(new Date().clearTime());
20166     },
20167     
20168     resize : function() {
20169         var sz  = this.el.getSize();
20170         
20171         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20172         this.el.select('.fc-day-content div',true).setHeight(34);
20173     },
20174     
20175     
20176     // private
20177     showPrevMonth : function(e){
20178         this.update(this.activeDate.add("mo", -1));
20179     },
20180     showToday : function(e){
20181         this.update(new Date().clearTime());
20182     },
20183     // private
20184     showNextMonth : function(e){
20185         this.update(this.activeDate.add("mo", 1));
20186     },
20187
20188     // private
20189     showPrevYear : function(){
20190         this.update(this.activeDate.add("y", -1));
20191     },
20192
20193     // private
20194     showNextYear : function(){
20195         this.update(this.activeDate.add("y", 1));
20196     },
20197
20198     
20199    // private
20200     update : function(date)
20201     {
20202         var vd = this.activeDate;
20203         this.activeDate = date;
20204 //        if(vd && this.el){
20205 //            var t = date.getTime();
20206 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20207 //                Roo.log('using add remove');
20208 //                
20209 //                this.fireEvent('monthchange', this, date);
20210 //                
20211 //                this.cells.removeClass("fc-state-highlight");
20212 //                this.cells.each(function(c){
20213 //                   if(c.dateValue == t){
20214 //                       c.addClass("fc-state-highlight");
20215 //                       setTimeout(function(){
20216 //                            try{c.dom.firstChild.focus();}catch(e){}
20217 //                       }, 50);
20218 //                       return false;
20219 //                   }
20220 //                   return true;
20221 //                });
20222 //                return;
20223 //            }
20224 //        }
20225         
20226         var days = date.getDaysInMonth();
20227         
20228         var firstOfMonth = date.getFirstDateOfMonth();
20229         var startingPos = firstOfMonth.getDay()-this.startDay;
20230         
20231         if(startingPos < this.startDay){
20232             startingPos += 7;
20233         }
20234         
20235         var pm = date.add(Date.MONTH, -1);
20236         var prevStart = pm.getDaysInMonth()-startingPos;
20237 //        
20238         this.cells = this.el.select('.fc-day',true);
20239         this.textNodes = this.el.query('.fc-day-number');
20240         this.cells.addClassOnOver('fc-state-hover');
20241         
20242         var cells = this.cells.elements;
20243         var textEls = this.textNodes;
20244         
20245         Roo.each(cells, function(cell){
20246             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20247         });
20248         
20249         days += startingPos;
20250
20251         // convert everything to numbers so it's fast
20252         var day = 86400000;
20253         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20254         //Roo.log(d);
20255         //Roo.log(pm);
20256         //Roo.log(prevStart);
20257         
20258         var today = new Date().clearTime().getTime();
20259         var sel = date.clearTime().getTime();
20260         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20261         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20262         var ddMatch = this.disabledDatesRE;
20263         var ddText = this.disabledDatesText;
20264         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20265         var ddaysText = this.disabledDaysText;
20266         var format = this.format;
20267         
20268         var setCellClass = function(cal, cell){
20269             cell.row = 0;
20270             cell.events = [];
20271             cell.more = [];
20272             //Roo.log('set Cell Class');
20273             cell.title = "";
20274             var t = d.getTime();
20275             
20276             //Roo.log(d);
20277             
20278             cell.dateValue = t;
20279             if(t == today){
20280                 cell.className += " fc-today";
20281                 cell.className += " fc-state-highlight";
20282                 cell.title = cal.todayText;
20283             }
20284             if(t == sel){
20285                 // disable highlight in other month..
20286                 //cell.className += " fc-state-highlight";
20287                 
20288             }
20289             // disabling
20290             if(t < min) {
20291                 cell.className = " fc-state-disabled";
20292                 cell.title = cal.minText;
20293                 return;
20294             }
20295             if(t > max) {
20296                 cell.className = " fc-state-disabled";
20297                 cell.title = cal.maxText;
20298                 return;
20299             }
20300             if(ddays){
20301                 if(ddays.indexOf(d.getDay()) != -1){
20302                     cell.title = ddaysText;
20303                     cell.className = " fc-state-disabled";
20304                 }
20305             }
20306             if(ddMatch && format){
20307                 var fvalue = d.dateFormat(format);
20308                 if(ddMatch.test(fvalue)){
20309                     cell.title = ddText.replace("%0", fvalue);
20310                     cell.className = " fc-state-disabled";
20311                 }
20312             }
20313             
20314             if (!cell.initialClassName) {
20315                 cell.initialClassName = cell.dom.className;
20316             }
20317             
20318             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20319         };
20320
20321         var i = 0;
20322         
20323         for(; i < startingPos; i++) {
20324             textEls[i].innerHTML = (++prevStart);
20325             d.setDate(d.getDate()+1);
20326             
20327             cells[i].className = "fc-past fc-other-month";
20328             setCellClass(this, cells[i]);
20329         }
20330         
20331         var intDay = 0;
20332         
20333         for(; i < days; i++){
20334             intDay = i - startingPos + 1;
20335             textEls[i].innerHTML = (intDay);
20336             d.setDate(d.getDate()+1);
20337             
20338             cells[i].className = ''; // "x-date-active";
20339             setCellClass(this, cells[i]);
20340         }
20341         var extraDays = 0;
20342         
20343         for(; i < 42; i++) {
20344             textEls[i].innerHTML = (++extraDays);
20345             d.setDate(d.getDate()+1);
20346             
20347             cells[i].className = "fc-future fc-other-month";
20348             setCellClass(this, cells[i]);
20349         }
20350         
20351         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20352         
20353         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20354         
20355         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20356         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20357         
20358         if(totalRows != 6){
20359             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20360             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20361         }
20362         
20363         this.fireEvent('monthchange', this, date);
20364         
20365         
20366         /*
20367         if(!this.internalRender){
20368             var main = this.el.dom.firstChild;
20369             var w = main.offsetWidth;
20370             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20371             Roo.fly(main).setWidth(w);
20372             this.internalRender = true;
20373             // opera does not respect the auto grow header center column
20374             // then, after it gets a width opera refuses to recalculate
20375             // without a second pass
20376             if(Roo.isOpera && !this.secondPass){
20377                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20378                 this.secondPass = true;
20379                 this.update.defer(10, this, [date]);
20380             }
20381         }
20382         */
20383         
20384     },
20385     
20386     findCell : function(dt) {
20387         dt = dt.clearTime().getTime();
20388         var ret = false;
20389         this.cells.each(function(c){
20390             //Roo.log("check " +c.dateValue + '?=' + dt);
20391             if(c.dateValue == dt){
20392                 ret = c;
20393                 return false;
20394             }
20395             return true;
20396         });
20397         
20398         return ret;
20399     },
20400     
20401     findCells : function(ev) {
20402         var s = ev.start.clone().clearTime().getTime();
20403        // Roo.log(s);
20404         var e= ev.end.clone().clearTime().getTime();
20405        // Roo.log(e);
20406         var ret = [];
20407         this.cells.each(function(c){
20408              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20409             
20410             if(c.dateValue > e){
20411                 return ;
20412             }
20413             if(c.dateValue < s){
20414                 return ;
20415             }
20416             ret.push(c);
20417         });
20418         
20419         return ret;    
20420     },
20421     
20422 //    findBestRow: function(cells)
20423 //    {
20424 //        var ret = 0;
20425 //        
20426 //        for (var i =0 ; i < cells.length;i++) {
20427 //            ret  = Math.max(cells[i].rows || 0,ret);
20428 //        }
20429 //        return ret;
20430 //        
20431 //    },
20432     
20433     
20434     addItem : function(ev)
20435     {
20436         // look for vertical location slot in
20437         var cells = this.findCells(ev);
20438         
20439 //        ev.row = this.findBestRow(cells);
20440         
20441         // work out the location.
20442         
20443         var crow = false;
20444         var rows = [];
20445         for(var i =0; i < cells.length; i++) {
20446             
20447             cells[i].row = cells[0].row;
20448             
20449             if(i == 0){
20450                 cells[i].row = cells[i].row + 1;
20451             }
20452             
20453             if (!crow) {
20454                 crow = {
20455                     start : cells[i],
20456                     end :  cells[i]
20457                 };
20458                 continue;
20459             }
20460             if (crow.start.getY() == cells[i].getY()) {
20461                 // on same row.
20462                 crow.end = cells[i];
20463                 continue;
20464             }
20465             // different row.
20466             rows.push(crow);
20467             crow = {
20468                 start: cells[i],
20469                 end : cells[i]
20470             };
20471             
20472         }
20473         
20474         rows.push(crow);
20475         ev.els = [];
20476         ev.rows = rows;
20477         ev.cells = cells;
20478         
20479         cells[0].events.push(ev);
20480         
20481         this.calevents.push(ev);
20482     },
20483     
20484     clearEvents: function() {
20485         
20486         if(!this.calevents){
20487             return;
20488         }
20489         
20490         Roo.each(this.cells.elements, function(c){
20491             c.row = 0;
20492             c.events = [];
20493             c.more = [];
20494         });
20495         
20496         Roo.each(this.calevents, function(e) {
20497             Roo.each(e.els, function(el) {
20498                 el.un('mouseenter' ,this.onEventEnter, this);
20499                 el.un('mouseleave' ,this.onEventLeave, this);
20500                 el.remove();
20501             },this);
20502         },this);
20503         
20504         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20505             e.remove();
20506         });
20507         
20508     },
20509     
20510     renderEvents: function()
20511     {   
20512         var _this = this;
20513         
20514         this.cells.each(function(c) {
20515             
20516             if(c.row < 5){
20517                 return;
20518             }
20519             
20520             var ev = c.events;
20521             
20522             var r = 4;
20523             if(c.row != c.events.length){
20524                 r = 4 - (4 - (c.row - c.events.length));
20525             }
20526             
20527             c.events = ev.slice(0, r);
20528             c.more = ev.slice(r);
20529             
20530             if(c.more.length && c.more.length == 1){
20531                 c.events.push(c.more.pop());
20532             }
20533             
20534             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20535             
20536         });
20537             
20538         this.cells.each(function(c) {
20539             
20540             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20541             
20542             
20543             for (var e = 0; e < c.events.length; e++){
20544                 var ev = c.events[e];
20545                 var rows = ev.rows;
20546                 
20547                 for(var i = 0; i < rows.length; i++) {
20548                 
20549                     // how many rows should it span..
20550
20551                     var  cfg = {
20552                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20553                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20554
20555                         unselectable : "on",
20556                         cn : [
20557                             {
20558                                 cls: 'fc-event-inner',
20559                                 cn : [
20560     //                                {
20561     //                                  tag:'span',
20562     //                                  cls: 'fc-event-time',
20563     //                                  html : cells.length > 1 ? '' : ev.time
20564     //                                },
20565                                     {
20566                                       tag:'span',
20567                                       cls: 'fc-event-title',
20568                                       html : String.format('{0}', ev.title)
20569                                     }
20570
20571
20572                                 ]
20573                             },
20574                             {
20575                                 cls: 'ui-resizable-handle ui-resizable-e',
20576                                 html : '&nbsp;&nbsp;&nbsp'
20577                             }
20578
20579                         ]
20580                     };
20581
20582                     if (i == 0) {
20583                         cfg.cls += ' fc-event-start';
20584                     }
20585                     if ((i+1) == rows.length) {
20586                         cfg.cls += ' fc-event-end';
20587                     }
20588
20589                     var ctr = _this.el.select('.fc-event-container',true).first();
20590                     var cg = ctr.createChild(cfg);
20591
20592                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20593                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20594
20595                     var r = (c.more.length) ? 1 : 0;
20596                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20597                     cg.setWidth(ebox.right - sbox.x -2);
20598
20599                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20600                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20601                     cg.on('click', _this.onEventClick, _this, ev);
20602
20603                     ev.els.push(cg);
20604                     
20605                 }
20606                 
20607             }
20608             
20609             
20610             if(c.more.length){
20611                 var  cfg = {
20612                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20613                     style : 'position: absolute',
20614                     unselectable : "on",
20615                     cn : [
20616                         {
20617                             cls: 'fc-event-inner',
20618                             cn : [
20619                                 {
20620                                   tag:'span',
20621                                   cls: 'fc-event-title',
20622                                   html : 'More'
20623                                 }
20624
20625
20626                             ]
20627                         },
20628                         {
20629                             cls: 'ui-resizable-handle ui-resizable-e',
20630                             html : '&nbsp;&nbsp;&nbsp'
20631                         }
20632
20633                     ]
20634                 };
20635
20636                 var ctr = _this.el.select('.fc-event-container',true).first();
20637                 var cg = ctr.createChild(cfg);
20638
20639                 var sbox = c.select('.fc-day-content',true).first().getBox();
20640                 var ebox = c.select('.fc-day-content',true).first().getBox();
20641                 //Roo.log(cg);
20642                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20643                 cg.setWidth(ebox.right - sbox.x -2);
20644
20645                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20646                 
20647             }
20648             
20649         });
20650         
20651         
20652         
20653     },
20654     
20655     onEventEnter: function (e, el,event,d) {
20656         this.fireEvent('evententer', this, el, event);
20657     },
20658     
20659     onEventLeave: function (e, el,event,d) {
20660         this.fireEvent('eventleave', this, el, event);
20661     },
20662     
20663     onEventClick: function (e, el,event,d) {
20664         this.fireEvent('eventclick', this, el, event);
20665     },
20666     
20667     onMonthChange: function () {
20668         this.store.load();
20669     },
20670     
20671     onMoreEventClick: function(e, el, more)
20672     {
20673         var _this = this;
20674         
20675         this.calpopover.placement = 'right';
20676         this.calpopover.setTitle('More');
20677         
20678         this.calpopover.setContent('');
20679         
20680         var ctr = this.calpopover.el.select('.popover-content', true).first();
20681         
20682         Roo.each(more, function(m){
20683             var cfg = {
20684                 cls : 'fc-event-hori fc-event-draggable',
20685                 html : m.title
20686             };
20687             var cg = ctr.createChild(cfg);
20688             
20689             cg.on('click', _this.onEventClick, _this, m);
20690         });
20691         
20692         this.calpopover.show(el);
20693         
20694         
20695     },
20696     
20697     onLoad: function () 
20698     {   
20699         this.calevents = [];
20700         var cal = this;
20701         
20702         if(this.store.getCount() > 0){
20703             this.store.data.each(function(d){
20704                cal.addItem({
20705                     id : d.data.id,
20706                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20707                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20708                     time : d.data.start_time,
20709                     title : d.data.title,
20710                     description : d.data.description,
20711                     venue : d.data.venue
20712                 });
20713             });
20714         }
20715         
20716         this.renderEvents();
20717         
20718         if(this.calevents.length && this.loadMask){
20719             this.maskEl.hide();
20720         }
20721     },
20722     
20723     onBeforeLoad: function()
20724     {
20725         this.clearEvents();
20726         if(this.loadMask){
20727             this.maskEl.show();
20728         }
20729     }
20730 });
20731
20732  
20733  /*
20734  * - LGPL
20735  *
20736  * element
20737  * 
20738  */
20739
20740 /**
20741  * @class Roo.bootstrap.Popover
20742  * @extends Roo.bootstrap.Component
20743  * Bootstrap Popover class
20744  * @cfg {String} html contents of the popover   (or false to use children..)
20745  * @cfg {String} title of popover (or false to hide)
20746  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20747  * @cfg {String} trigger click || hover (or false to trigger manually)
20748  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20749  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20750  *      - if false and it has a 'parent' then it will be automatically added to that element
20751  *      - if string - Roo.get  will be called 
20752  * @cfg {Number} delay - delay before showing
20753  
20754  * @constructor
20755  * Create a new Popover
20756  * @param {Object} config The config object
20757  */
20758
20759 Roo.bootstrap.Popover = function(config){
20760     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20761     
20762     this.addEvents({
20763         // raw events
20764          /**
20765          * @event show
20766          * After the popover show
20767          * 
20768          * @param {Roo.bootstrap.Popover} this
20769          */
20770         "show" : true,
20771         /**
20772          * @event hide
20773          * After the popover hide
20774          * 
20775          * @param {Roo.bootstrap.Popover} this
20776          */
20777         "hide" : true
20778     });
20779 };
20780
20781 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20782     
20783     title: false,
20784     html: false,
20785     
20786     placement : 'right',
20787     trigger : 'hover', // hover
20788     modal : false,
20789     delay : 0,
20790     
20791     over: false,
20792     
20793     can_build_overlaid : false,
20794     
20795     maskEl : false, // the mask element
20796     headerEl : false,
20797     contentEl : false,
20798     alignEl : false, // when show is called with an element - this get's stored.
20799     
20800     getChildContainer : function()
20801     {
20802         return this.contentEl;
20803         
20804     },
20805     getPopoverHeader : function()
20806     {
20807         this.title = true; // flag not to hide it..
20808         this.headerEl.addClass('p-0');
20809         return this.headerEl
20810     },
20811     
20812     
20813     getAutoCreate : function(){
20814          
20815         var cfg = {
20816            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20817            style: 'display:block',
20818            cn : [
20819                 {
20820                     cls : 'arrow'
20821                 },
20822                 {
20823                     cls : 'popover-inner ',
20824                     cn : [
20825                         {
20826                             tag: 'h3',
20827                             cls: 'popover-title popover-header',
20828                             html : this.title === false ? '' : this.title
20829                         },
20830                         {
20831                             cls : 'popover-content popover-body '  + (this.cls || ''),
20832                             html : this.html || ''
20833                         }
20834                     ]
20835                     
20836                 }
20837            ]
20838         };
20839         
20840         return cfg;
20841     },
20842     /**
20843      * @param {string} the title
20844      */
20845     setTitle: function(str)
20846     {
20847         this.title = str;
20848         if (this.el) {
20849             this.headerEl.dom.innerHTML = str;
20850         }
20851         
20852     },
20853     /**
20854      * @param {string} the body content
20855      */
20856     setContent: function(str)
20857     {
20858         this.html = str;
20859         if (this.contentEl) {
20860             this.contentEl.dom.innerHTML = str;
20861         }
20862         
20863     },
20864     // as it get's added to the bottom of the page.
20865     onRender : function(ct, position)
20866     {
20867         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20868         
20869         
20870         
20871         if(!this.el){
20872             var cfg = Roo.apply({},  this.getAutoCreate());
20873             cfg.id = Roo.id();
20874             
20875             if (this.cls) {
20876                 cfg.cls += ' ' + this.cls;
20877             }
20878             if (this.style) {
20879                 cfg.style = this.style;
20880             }
20881             //Roo.log("adding to ");
20882             this.el = Roo.get(document.body).createChild(cfg, position);
20883 //            Roo.log(this.el);
20884         }
20885         
20886         this.contentEl = this.el.select('.popover-content',true).first();
20887         this.headerEl =  this.el.select('.popover-title',true).first();
20888         
20889         var nitems = [];
20890         if(typeof(this.items) != 'undefined'){
20891             var items = this.items;
20892             delete this.items;
20893
20894             for(var i =0;i < items.length;i++) {
20895                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20896             }
20897         }
20898
20899         this.items = nitems;
20900         
20901         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20902         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20903         
20904         
20905         
20906         this.initEvents();
20907     },
20908     
20909     resizeMask : function()
20910     {
20911         this.maskEl.setSize(
20912             Roo.lib.Dom.getViewWidth(true),
20913             Roo.lib.Dom.getViewHeight(true)
20914         );
20915     },
20916     
20917     initEvents : function()
20918     {
20919         
20920         if (!this.modal) { 
20921             Roo.bootstrap.Popover.register(this);
20922         }
20923          
20924         this.arrowEl = this.el.select('.arrow',true).first();
20925         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20926         this.el.enableDisplayMode('block');
20927         this.el.hide();
20928  
20929         
20930         if (this.over === false && !this.parent()) {
20931             return; 
20932         }
20933         if (this.triggers === false) {
20934             return;
20935         }
20936          
20937         // support parent
20938         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20939         var triggers = this.trigger ? this.trigger.split(' ') : [];
20940         Roo.each(triggers, function(trigger) {
20941         
20942             if (trigger == 'click') {
20943                 on_el.on('click', this.toggle, this);
20944             } else if (trigger != 'manual') {
20945                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20946                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20947       
20948                 on_el.on(eventIn  ,this.enter, this);
20949                 on_el.on(eventOut, this.leave, this);
20950             }
20951         }, this);
20952     },
20953     
20954     
20955     // private
20956     timeout : null,
20957     hoverState : null,
20958     
20959     toggle : function () {
20960         this.hoverState == 'in' ? this.leave() : this.enter();
20961     },
20962     
20963     enter : function () {
20964         
20965         clearTimeout(this.timeout);
20966     
20967         this.hoverState = 'in';
20968     
20969         if (!this.delay || !this.delay.show) {
20970             this.show();
20971             return;
20972         }
20973         var _t = this;
20974         this.timeout = setTimeout(function () {
20975             if (_t.hoverState == 'in') {
20976                 _t.show();
20977             }
20978         }, this.delay.show)
20979     },
20980     
20981     leave : function() {
20982         clearTimeout(this.timeout);
20983     
20984         this.hoverState = 'out';
20985     
20986         if (!this.delay || !this.delay.hide) {
20987             this.hide();
20988             return;
20989         }
20990         var _t = this;
20991         this.timeout = setTimeout(function () {
20992             if (_t.hoverState == 'out') {
20993                 _t.hide();
20994             }
20995         }, this.delay.hide)
20996     },
20997     /**
20998      * Show the popover
20999      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21000      * @param {string} (left|right|top|bottom) position
21001      */
21002     show : function (on_el, placement)
21003     {
21004         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21005         on_el = on_el || false; // default to false
21006          
21007         if (!on_el) {
21008             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21009                 on_el = this.parent().el;
21010             } else if (this.over) {
21011                 on_el = Roo.get(this.over);
21012             }
21013             
21014         }
21015         
21016         this.alignEl = Roo.get( on_el );
21017
21018         if (!this.el) {
21019             this.render(document.body);
21020         }
21021         
21022         
21023          
21024         
21025         if (this.title === false) {
21026             this.headerEl.hide();
21027         }
21028         
21029        
21030         this.el.show();
21031         this.el.dom.style.display = 'block';
21032          
21033  
21034         if (this.alignEl) {
21035             this.updatePosition(this.placement, true);
21036              
21037         } else {
21038             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21039             var es = this.el.getSize();
21040             var x = Roo.lib.Dom.getViewWidth()/2;
21041             var y = Roo.lib.Dom.getViewHeight()/2;
21042             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21043             
21044         }
21045
21046         
21047         //var arrow = this.el.select('.arrow',true).first();
21048         //arrow.set(align[2], 
21049         
21050         this.el.addClass('in');
21051         
21052          
21053         
21054         this.hoverState = 'in';
21055         
21056         if (this.modal) {
21057             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21058             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21059             this.maskEl.dom.style.display = 'block';
21060             this.maskEl.addClass('show');
21061         }
21062         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21063  
21064         this.fireEvent('show', this);
21065         
21066     },
21067     /**
21068      * fire this manually after loading a grid in the table for example
21069      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21070      * @param {Boolean} try and move it if we cant get right position.
21071      */
21072     updatePosition : function(placement, try_move)
21073     {
21074         // allow for calling with no parameters
21075         placement = placement   ? placement :  this.placement;
21076         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21077         
21078         this.el.removeClass([
21079             'fade','top','bottom', 'left', 'right','in',
21080             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21081         ]);
21082         this.el.addClass(placement + ' bs-popover-' + placement);
21083         
21084         if (!this.alignEl ) {
21085             return false;
21086         }
21087         
21088         switch (placement) {
21089             case 'right':
21090                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21091                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21092                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21093                     //normal display... or moved up/down.
21094                     this.el.setXY(offset);
21095                     var xy = this.alignEl.getAnchorXY('tr', false);
21096                     xy[0]+=2;xy[1]+=5;
21097                     this.arrowEl.setXY(xy);
21098                     return true;
21099                 }
21100                 // continue through...
21101                 return this.updatePosition('left', false);
21102                 
21103             
21104             case 'left':
21105                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21106                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21107                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21108                     //normal display... or moved up/down.
21109                     this.el.setXY(offset);
21110                     var xy = this.alignEl.getAnchorXY('tl', false);
21111                     xy[0]-=10;xy[1]+=5; // << fix me
21112                     this.arrowEl.setXY(xy);
21113                     return true;
21114                 }
21115                 // call self...
21116                 return this.updatePosition('right', false);
21117             
21118             case 'top':
21119                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21120                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21121                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21122                     //normal display... or moved up/down.
21123                     this.el.setXY(offset);
21124                     var xy = this.alignEl.getAnchorXY('t', false);
21125                     xy[1]-=10; // << fix me
21126                     this.arrowEl.setXY(xy);
21127                     return true;
21128                 }
21129                 // fall through
21130                return this.updatePosition('bottom', false);
21131             
21132             case 'bottom':
21133                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21134                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21135                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21136                     //normal display... or moved up/down.
21137                     this.el.setXY(offset);
21138                     var xy = this.alignEl.getAnchorXY('b', false);
21139                      xy[1]+=2; // << fix me
21140                     this.arrowEl.setXY(xy);
21141                     return true;
21142                 }
21143                 // fall through
21144                 return this.updatePosition('top', false);
21145                 
21146             
21147         }
21148         
21149         
21150         return false;
21151     },
21152     
21153     hide : function()
21154     {
21155         this.el.setXY([0,0]);
21156         this.el.removeClass('in');
21157         this.el.hide();
21158         this.hoverState = null;
21159         this.maskEl.hide(); // always..
21160         this.fireEvent('hide', this);
21161     }
21162     
21163 });
21164
21165
21166 Roo.apply(Roo.bootstrap.Popover, {
21167
21168     alignment : {
21169         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21170         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21171         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21172         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21173     },
21174     
21175     zIndex : 20001,
21176
21177     clickHander : false,
21178     
21179     
21180
21181     onMouseDown : function(e)
21182     {
21183         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21184             /// what is nothing is showing..
21185             this.hideAll();
21186         }
21187          
21188     },
21189     
21190     
21191     popups : [],
21192     
21193     register : function(popup)
21194     {
21195         if (!Roo.bootstrap.Popover.clickHandler) {
21196             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21197         }
21198         // hide other popups.
21199         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21200         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21201         this.hideAll(); //<< why?
21202         //this.popups.push(popup);
21203     },
21204     hideAll : function()
21205     {
21206         this.popups.forEach(function(p) {
21207             p.hide();
21208         });
21209     },
21210     onShow : function() {
21211         Roo.bootstrap.Popover.popups.push(this);
21212     },
21213     onHide : function() {
21214         Roo.bootstrap.Popover.popups.remove(this);
21215     } 
21216
21217 });/*
21218  * - LGPL
21219  *
21220  * Card header - holder for the card header elements.
21221  * 
21222  */
21223
21224 /**
21225  * @class Roo.bootstrap.PopoverNav
21226  * @extends Roo.bootstrap.NavGroup
21227  * Bootstrap Popover header navigation class
21228  * @constructor
21229  * Create a new Popover Header Navigation 
21230  * @param {Object} config The config object
21231  */
21232
21233 Roo.bootstrap.PopoverNav = function(config){
21234     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21235 };
21236
21237 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21238     
21239     
21240     container_method : 'getPopoverHeader' 
21241     
21242      
21243     
21244     
21245    
21246 });
21247
21248  
21249
21250  /*
21251  * - LGPL
21252  *
21253  * Progress
21254  * 
21255  */
21256
21257 /**
21258  * @class Roo.bootstrap.Progress
21259  * @extends Roo.bootstrap.Component
21260  * Bootstrap Progress class
21261  * @cfg {Boolean} striped striped of the progress bar
21262  * @cfg {Boolean} active animated of the progress bar
21263  * 
21264  * 
21265  * @constructor
21266  * Create a new Progress
21267  * @param {Object} config The config object
21268  */
21269
21270 Roo.bootstrap.Progress = function(config){
21271     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21272 };
21273
21274 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21275     
21276     striped : false,
21277     active: false,
21278     
21279     getAutoCreate : function(){
21280         var cfg = {
21281             tag: 'div',
21282             cls: 'progress'
21283         };
21284         
21285         
21286         if(this.striped){
21287             cfg.cls += ' progress-striped';
21288         }
21289       
21290         if(this.active){
21291             cfg.cls += ' active';
21292         }
21293         
21294         
21295         return cfg;
21296     }
21297    
21298 });
21299
21300  
21301
21302  /*
21303  * - LGPL
21304  *
21305  * ProgressBar
21306  * 
21307  */
21308
21309 /**
21310  * @class Roo.bootstrap.ProgressBar
21311  * @extends Roo.bootstrap.Component
21312  * Bootstrap ProgressBar class
21313  * @cfg {Number} aria_valuenow aria-value now
21314  * @cfg {Number} aria_valuemin aria-value min
21315  * @cfg {Number} aria_valuemax aria-value max
21316  * @cfg {String} label label for the progress bar
21317  * @cfg {String} panel (success | info | warning | danger )
21318  * @cfg {String} role role of the progress bar
21319  * @cfg {String} sr_only text
21320  * 
21321  * 
21322  * @constructor
21323  * Create a new ProgressBar
21324  * @param {Object} config The config object
21325  */
21326
21327 Roo.bootstrap.ProgressBar = function(config){
21328     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21329 };
21330
21331 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21332     
21333     aria_valuenow : 0,
21334     aria_valuemin : 0,
21335     aria_valuemax : 100,
21336     label : false,
21337     panel : false,
21338     role : false,
21339     sr_only: false,
21340     
21341     getAutoCreate : function()
21342     {
21343         
21344         var cfg = {
21345             tag: 'div',
21346             cls: 'progress-bar',
21347             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21348         };
21349         
21350         if(this.sr_only){
21351             cfg.cn = {
21352                 tag: 'span',
21353                 cls: 'sr-only',
21354                 html: this.sr_only
21355             }
21356         }
21357         
21358         if(this.role){
21359             cfg.role = this.role;
21360         }
21361         
21362         if(this.aria_valuenow){
21363             cfg['aria-valuenow'] = this.aria_valuenow;
21364         }
21365         
21366         if(this.aria_valuemin){
21367             cfg['aria-valuemin'] = this.aria_valuemin;
21368         }
21369         
21370         if(this.aria_valuemax){
21371             cfg['aria-valuemax'] = this.aria_valuemax;
21372         }
21373         
21374         if(this.label && !this.sr_only){
21375             cfg.html = this.label;
21376         }
21377         
21378         if(this.panel){
21379             cfg.cls += ' progress-bar-' + this.panel;
21380         }
21381         
21382         return cfg;
21383     },
21384     
21385     update : function(aria_valuenow)
21386     {
21387         this.aria_valuenow = aria_valuenow;
21388         
21389         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21390     }
21391    
21392 });
21393
21394  
21395
21396  /*
21397  * - LGPL
21398  *
21399  * column
21400  * 
21401  */
21402
21403 /**
21404  * @class Roo.bootstrap.TabGroup
21405  * @extends Roo.bootstrap.Column
21406  * Bootstrap Column class
21407  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21408  * @cfg {Boolean} carousel true to make the group behave like a carousel
21409  * @cfg {Boolean} bullets show bullets for the panels
21410  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21411  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21412  * @cfg {Boolean} showarrow (true|false) show arrow default true
21413  * 
21414  * @constructor
21415  * Create a new TabGroup
21416  * @param {Object} config The config object
21417  */
21418
21419 Roo.bootstrap.TabGroup = function(config){
21420     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21421     if (!this.navId) {
21422         this.navId = Roo.id();
21423     }
21424     this.tabs = [];
21425     Roo.bootstrap.TabGroup.register(this);
21426     
21427 };
21428
21429 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21430     
21431     carousel : false,
21432     transition : false,
21433     bullets : 0,
21434     timer : 0,
21435     autoslide : false,
21436     slideFn : false,
21437     slideOnTouch : false,
21438     showarrow : true,
21439     
21440     getAutoCreate : function()
21441     {
21442         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21443         
21444         cfg.cls += ' tab-content';
21445         
21446         if (this.carousel) {
21447             cfg.cls += ' carousel slide';
21448             
21449             cfg.cn = [{
21450                cls : 'carousel-inner',
21451                cn : []
21452             }];
21453         
21454             if(this.bullets  && !Roo.isTouch){
21455                 
21456                 var bullets = {
21457                     cls : 'carousel-bullets',
21458                     cn : []
21459                 };
21460                
21461                 if(this.bullets_cls){
21462                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21463                 }
21464                 
21465                 bullets.cn.push({
21466                     cls : 'clear'
21467                 });
21468                 
21469                 cfg.cn[0].cn.push(bullets);
21470             }
21471             
21472             if(this.showarrow){
21473                 cfg.cn[0].cn.push({
21474                     tag : 'div',
21475                     class : 'carousel-arrow',
21476                     cn : [
21477                         {
21478                             tag : 'div',
21479                             class : 'carousel-prev',
21480                             cn : [
21481                                 {
21482                                     tag : 'i',
21483                                     class : 'fa fa-chevron-left'
21484                                 }
21485                             ]
21486                         },
21487                         {
21488                             tag : 'div',
21489                             class : 'carousel-next',
21490                             cn : [
21491                                 {
21492                                     tag : 'i',
21493                                     class : 'fa fa-chevron-right'
21494                                 }
21495                             ]
21496                         }
21497                     ]
21498                 });
21499             }
21500             
21501         }
21502         
21503         return cfg;
21504     },
21505     
21506     initEvents:  function()
21507     {
21508 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21509 //            this.el.on("touchstart", this.onTouchStart, this);
21510 //        }
21511         
21512         if(this.autoslide){
21513             var _this = this;
21514             
21515             this.slideFn = window.setInterval(function() {
21516                 _this.showPanelNext();
21517             }, this.timer);
21518         }
21519         
21520         if(this.showarrow){
21521             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21522             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21523         }
21524         
21525         
21526     },
21527     
21528 //    onTouchStart : function(e, el, o)
21529 //    {
21530 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21531 //            return;
21532 //        }
21533 //        
21534 //        this.showPanelNext();
21535 //    },
21536     
21537     
21538     getChildContainer : function()
21539     {
21540         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21541     },
21542     
21543     /**
21544     * register a Navigation item
21545     * @param {Roo.bootstrap.NavItem} the navitem to add
21546     */
21547     register : function(item)
21548     {
21549         this.tabs.push( item);
21550         item.navId = this.navId; // not really needed..
21551         this.addBullet();
21552     
21553     },
21554     
21555     getActivePanel : function()
21556     {
21557         var r = false;
21558         Roo.each(this.tabs, function(t) {
21559             if (t.active) {
21560                 r = t;
21561                 return false;
21562             }
21563             return null;
21564         });
21565         return r;
21566         
21567     },
21568     getPanelByName : function(n)
21569     {
21570         var r = false;
21571         Roo.each(this.tabs, function(t) {
21572             if (t.tabId == n) {
21573                 r = t;
21574                 return false;
21575             }
21576             return null;
21577         });
21578         return r;
21579     },
21580     indexOfPanel : function(p)
21581     {
21582         var r = false;
21583         Roo.each(this.tabs, function(t,i) {
21584             if (t.tabId == p.tabId) {
21585                 r = i;
21586                 return false;
21587             }
21588             return null;
21589         });
21590         return r;
21591     },
21592     /**
21593      * show a specific panel
21594      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21595      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21596      */
21597     showPanel : function (pan)
21598     {
21599         if(this.transition || typeof(pan) == 'undefined'){
21600             Roo.log("waiting for the transitionend");
21601             return false;
21602         }
21603         
21604         if (typeof(pan) == 'number') {
21605             pan = this.tabs[pan];
21606         }
21607         
21608         if (typeof(pan) == 'string') {
21609             pan = this.getPanelByName(pan);
21610         }
21611         
21612         var cur = this.getActivePanel();
21613         
21614         if(!pan || !cur){
21615             Roo.log('pan or acitve pan is undefined');
21616             return false;
21617         }
21618         
21619         if (pan.tabId == this.getActivePanel().tabId) {
21620             return true;
21621         }
21622         
21623         if (false === cur.fireEvent('beforedeactivate')) {
21624             return false;
21625         }
21626         
21627         if(this.bullets > 0 && !Roo.isTouch){
21628             this.setActiveBullet(this.indexOfPanel(pan));
21629         }
21630         
21631         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21632             
21633             //class="carousel-item carousel-item-next carousel-item-left"
21634             
21635             this.transition = true;
21636             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21637             var lr = dir == 'next' ? 'left' : 'right';
21638             pan.el.addClass(dir); // or prev
21639             pan.el.addClass('carousel-item-' + dir); // or prev
21640             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21641             cur.el.addClass(lr); // or right
21642             pan.el.addClass(lr);
21643             cur.el.addClass('carousel-item-' +lr); // or right
21644             pan.el.addClass('carousel-item-' +lr);
21645             
21646             
21647             var _this = this;
21648             cur.el.on('transitionend', function() {
21649                 Roo.log("trans end?");
21650                 
21651                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21652                 pan.setActive(true);
21653                 
21654                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21655                 cur.setActive(false);
21656                 
21657                 _this.transition = false;
21658                 
21659             }, this, { single:  true } );
21660             
21661             return true;
21662         }
21663         
21664         cur.setActive(false);
21665         pan.setActive(true);
21666         
21667         return true;
21668         
21669     },
21670     showPanelNext : function()
21671     {
21672         var i = this.indexOfPanel(this.getActivePanel());
21673         
21674         if (i >= this.tabs.length - 1 && !this.autoslide) {
21675             return;
21676         }
21677         
21678         if (i >= this.tabs.length - 1 && this.autoslide) {
21679             i = -1;
21680         }
21681         
21682         this.showPanel(this.tabs[i+1]);
21683     },
21684     
21685     showPanelPrev : function()
21686     {
21687         var i = this.indexOfPanel(this.getActivePanel());
21688         
21689         if (i  < 1 && !this.autoslide) {
21690             return;
21691         }
21692         
21693         if (i < 1 && this.autoslide) {
21694             i = this.tabs.length;
21695         }
21696         
21697         this.showPanel(this.tabs[i-1]);
21698     },
21699     
21700     
21701     addBullet: function()
21702     {
21703         if(!this.bullets || Roo.isTouch){
21704             return;
21705         }
21706         var ctr = this.el.select('.carousel-bullets',true).first();
21707         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21708         var bullet = ctr.createChild({
21709             cls : 'bullet bullet-' + i
21710         },ctr.dom.lastChild);
21711         
21712         
21713         var _this = this;
21714         
21715         bullet.on('click', (function(e, el, o, ii, t){
21716
21717             e.preventDefault();
21718
21719             this.showPanel(ii);
21720
21721             if(this.autoslide && this.slideFn){
21722                 clearInterval(this.slideFn);
21723                 this.slideFn = window.setInterval(function() {
21724                     _this.showPanelNext();
21725                 }, this.timer);
21726             }
21727
21728         }).createDelegate(this, [i, bullet], true));
21729                 
21730         
21731     },
21732      
21733     setActiveBullet : function(i)
21734     {
21735         if(Roo.isTouch){
21736             return;
21737         }
21738         
21739         Roo.each(this.el.select('.bullet', true).elements, function(el){
21740             el.removeClass('selected');
21741         });
21742
21743         var bullet = this.el.select('.bullet-' + i, true).first();
21744         
21745         if(!bullet){
21746             return;
21747         }
21748         
21749         bullet.addClass('selected');
21750     }
21751     
21752     
21753   
21754 });
21755
21756  
21757
21758  
21759  
21760 Roo.apply(Roo.bootstrap.TabGroup, {
21761     
21762     groups: {},
21763      /**
21764     * register a Navigation Group
21765     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21766     */
21767     register : function(navgrp)
21768     {
21769         this.groups[navgrp.navId] = navgrp;
21770         
21771     },
21772     /**
21773     * fetch a Navigation Group based on the navigation ID
21774     * if one does not exist , it will get created.
21775     * @param {string} the navgroup to add
21776     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21777     */
21778     get: function(navId) {
21779         if (typeof(this.groups[navId]) == 'undefined') {
21780             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21781         }
21782         return this.groups[navId] ;
21783     }
21784     
21785     
21786     
21787 });
21788
21789  /*
21790  * - LGPL
21791  *
21792  * TabPanel
21793  * 
21794  */
21795
21796 /**
21797  * @class Roo.bootstrap.TabPanel
21798  * @extends Roo.bootstrap.Component
21799  * Bootstrap TabPanel class
21800  * @cfg {Boolean} active panel active
21801  * @cfg {String} html panel content
21802  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21803  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21804  * @cfg {String} href click to link..
21805  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21806  * 
21807  * 
21808  * @constructor
21809  * Create a new TabPanel
21810  * @param {Object} config The config object
21811  */
21812
21813 Roo.bootstrap.TabPanel = function(config){
21814     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21815     this.addEvents({
21816         /**
21817              * @event changed
21818              * Fires when the active status changes
21819              * @param {Roo.bootstrap.TabPanel} this
21820              * @param {Boolean} state the new state
21821             
21822          */
21823         'changed': true,
21824         /**
21825              * @event beforedeactivate
21826              * Fires before a tab is de-activated - can be used to do validation on a form.
21827              * @param {Roo.bootstrap.TabPanel} this
21828              * @return {Boolean} false if there is an error
21829             
21830          */
21831         'beforedeactivate': true
21832      });
21833     
21834     this.tabId = this.tabId || Roo.id();
21835   
21836 };
21837
21838 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21839     
21840     active: false,
21841     html: false,
21842     tabId: false,
21843     navId : false,
21844     href : '',
21845     touchSlide : false,
21846     getAutoCreate : function(){
21847         
21848         
21849         var cfg = {
21850             tag: 'div',
21851             // item is needed for carousel - not sure if it has any effect otherwise
21852             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21853             html: this.html || ''
21854         };
21855         
21856         if(this.active){
21857             cfg.cls += ' active';
21858         }
21859         
21860         if(this.tabId){
21861             cfg.tabId = this.tabId;
21862         }
21863         
21864         
21865         
21866         return cfg;
21867     },
21868     
21869     initEvents:  function()
21870     {
21871         var p = this.parent();
21872         
21873         this.navId = this.navId || p.navId;
21874         
21875         if (typeof(this.navId) != 'undefined') {
21876             // not really needed.. but just in case.. parent should be a NavGroup.
21877             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21878             
21879             tg.register(this);
21880             
21881             var i = tg.tabs.length - 1;
21882             
21883             if(this.active && tg.bullets > 0 && i < tg.bullets){
21884                 tg.setActiveBullet(i);
21885             }
21886         }
21887         
21888         this.el.on('click', this.onClick, this);
21889         
21890         if(Roo.isTouch && this.touchSlide){
21891             this.el.on("touchstart", this.onTouchStart, this);
21892             this.el.on("touchmove", this.onTouchMove, this);
21893             this.el.on("touchend", this.onTouchEnd, this);
21894         }
21895         
21896     },
21897     
21898     onRender : function(ct, position)
21899     {
21900         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21901     },
21902     
21903     setActive : function(state)
21904     {
21905         Roo.log("panel - set active " + this.tabId + "=" + state);
21906         
21907         this.active = state;
21908         if (!state) {
21909             this.el.removeClass('active');
21910             
21911         } else  if (!this.el.hasClass('active')) {
21912             this.el.addClass('active');
21913         }
21914         
21915         this.fireEvent('changed', this, state);
21916     },
21917     
21918     onClick : function(e)
21919     {
21920         e.preventDefault();
21921         
21922         if(!this.href.length){
21923             return;
21924         }
21925         
21926         window.location.href = this.href;
21927     },
21928     
21929     startX : 0,
21930     startY : 0,
21931     endX : 0,
21932     endY : 0,
21933     swiping : false,
21934     
21935     onTouchStart : function(e)
21936     {
21937         this.swiping = false;
21938         
21939         this.startX = e.browserEvent.touches[0].clientX;
21940         this.startY = e.browserEvent.touches[0].clientY;
21941     },
21942     
21943     onTouchMove : function(e)
21944     {
21945         this.swiping = true;
21946         
21947         this.endX = e.browserEvent.touches[0].clientX;
21948         this.endY = e.browserEvent.touches[0].clientY;
21949     },
21950     
21951     onTouchEnd : function(e)
21952     {
21953         if(!this.swiping){
21954             this.onClick(e);
21955             return;
21956         }
21957         
21958         var tabGroup = this.parent();
21959         
21960         if(this.endX > this.startX){ // swiping right
21961             tabGroup.showPanelPrev();
21962             return;
21963         }
21964         
21965         if(this.startX > this.endX){ // swiping left
21966             tabGroup.showPanelNext();
21967             return;
21968         }
21969     }
21970     
21971     
21972 });
21973  
21974
21975  
21976
21977  /*
21978  * - LGPL
21979  *
21980  * DateField
21981  * 
21982  */
21983
21984 /**
21985  * @class Roo.bootstrap.DateField
21986  * @extends Roo.bootstrap.Input
21987  * Bootstrap DateField class
21988  * @cfg {Number} weekStart default 0
21989  * @cfg {String} viewMode default empty, (months|years)
21990  * @cfg {String} minViewMode default empty, (months|years)
21991  * @cfg {Number} startDate default -Infinity
21992  * @cfg {Number} endDate default Infinity
21993  * @cfg {Boolean} todayHighlight default false
21994  * @cfg {Boolean} todayBtn default false
21995  * @cfg {Boolean} calendarWeeks default false
21996  * @cfg {Object} daysOfWeekDisabled default empty
21997  * @cfg {Boolean} singleMode default false (true | false)
21998  * 
21999  * @cfg {Boolean} keyboardNavigation default true
22000  * @cfg {String} language default en
22001  * 
22002  * @constructor
22003  * Create a new DateField
22004  * @param {Object} config The config object
22005  */
22006
22007 Roo.bootstrap.DateField = function(config){
22008     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22009      this.addEvents({
22010             /**
22011              * @event show
22012              * Fires when this field show.
22013              * @param {Roo.bootstrap.DateField} this
22014              * @param {Mixed} date The date value
22015              */
22016             show : true,
22017             /**
22018              * @event show
22019              * Fires when this field hide.
22020              * @param {Roo.bootstrap.DateField} this
22021              * @param {Mixed} date The date value
22022              */
22023             hide : true,
22024             /**
22025              * @event select
22026              * Fires when select a date.
22027              * @param {Roo.bootstrap.DateField} this
22028              * @param {Mixed} date The date value
22029              */
22030             select : true,
22031             /**
22032              * @event beforeselect
22033              * Fires when before select a date.
22034              * @param {Roo.bootstrap.DateField} this
22035              * @param {Mixed} date The date value
22036              */
22037             beforeselect : true
22038         });
22039 };
22040
22041 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22042     
22043     /**
22044      * @cfg {String} format
22045      * The default date format string which can be overriden for localization support.  The format must be
22046      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22047      */
22048     format : "m/d/y",
22049     /**
22050      * @cfg {String} altFormats
22051      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22052      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22053      */
22054     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22055     
22056     weekStart : 0,
22057     
22058     viewMode : '',
22059     
22060     minViewMode : '',
22061     
22062     todayHighlight : false,
22063     
22064     todayBtn: false,
22065     
22066     language: 'en',
22067     
22068     keyboardNavigation: true,
22069     
22070     calendarWeeks: false,
22071     
22072     startDate: -Infinity,
22073     
22074     endDate: Infinity,
22075     
22076     daysOfWeekDisabled: [],
22077     
22078     _events: [],
22079     
22080     singleMode : false,
22081     
22082     UTCDate: function()
22083     {
22084         return new Date(Date.UTC.apply(Date, arguments));
22085     },
22086     
22087     UTCToday: function()
22088     {
22089         var today = new Date();
22090         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22091     },
22092     
22093     getDate: function() {
22094             var d = this.getUTCDate();
22095             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22096     },
22097     
22098     getUTCDate: function() {
22099             return this.date;
22100     },
22101     
22102     setDate: function(d) {
22103             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22104     },
22105     
22106     setUTCDate: function(d) {
22107             this.date = d;
22108             this.setValue(this.formatDate(this.date));
22109     },
22110         
22111     onRender: function(ct, position)
22112     {
22113         
22114         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22115         
22116         this.language = this.language || 'en';
22117         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22118         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22119         
22120         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22121         this.format = this.format || 'm/d/y';
22122         this.isInline = false;
22123         this.isInput = true;
22124         this.component = this.el.select('.add-on', true).first() || false;
22125         this.component = (this.component && this.component.length === 0) ? false : this.component;
22126         this.hasInput = this.component && this.inputEl().length;
22127         
22128         if (typeof(this.minViewMode === 'string')) {
22129             switch (this.minViewMode) {
22130                 case 'months':
22131                     this.minViewMode = 1;
22132                     break;
22133                 case 'years':
22134                     this.minViewMode = 2;
22135                     break;
22136                 default:
22137                     this.minViewMode = 0;
22138                     break;
22139             }
22140         }
22141         
22142         if (typeof(this.viewMode === 'string')) {
22143             switch (this.viewMode) {
22144                 case 'months':
22145                     this.viewMode = 1;
22146                     break;
22147                 case 'years':
22148                     this.viewMode = 2;
22149                     break;
22150                 default:
22151                     this.viewMode = 0;
22152                     break;
22153             }
22154         }
22155                 
22156         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22157         
22158 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22159         
22160         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22161         
22162         this.picker().on('mousedown', this.onMousedown, this);
22163         this.picker().on('click', this.onClick, this);
22164         
22165         this.picker().addClass('datepicker-dropdown');
22166         
22167         this.startViewMode = this.viewMode;
22168         
22169         if(this.singleMode){
22170             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22171                 v.setVisibilityMode(Roo.Element.DISPLAY);
22172                 v.hide();
22173             });
22174             
22175             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22176                 v.setStyle('width', '189px');
22177             });
22178         }
22179         
22180         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22181             if(!this.calendarWeeks){
22182                 v.remove();
22183                 return;
22184             }
22185             
22186             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22187             v.attr('colspan', function(i, val){
22188                 return parseInt(val) + 1;
22189             });
22190         });
22191                         
22192         
22193         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22194         
22195         this.setStartDate(this.startDate);
22196         this.setEndDate(this.endDate);
22197         
22198         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22199         
22200         this.fillDow();
22201         this.fillMonths();
22202         this.update();
22203         this.showMode();
22204         
22205         if(this.isInline) {
22206             this.showPopup();
22207         }
22208     },
22209     
22210     picker : function()
22211     {
22212         return this.pickerEl;
22213 //        return this.el.select('.datepicker', true).first();
22214     },
22215     
22216     fillDow: function()
22217     {
22218         var dowCnt = this.weekStart;
22219         
22220         var dow = {
22221             tag: 'tr',
22222             cn: [
22223                 
22224             ]
22225         };
22226         
22227         if(this.calendarWeeks){
22228             dow.cn.push({
22229                 tag: 'th',
22230                 cls: 'cw',
22231                 html: '&nbsp;'
22232             })
22233         }
22234         
22235         while (dowCnt < this.weekStart + 7) {
22236             dow.cn.push({
22237                 tag: 'th',
22238                 cls: 'dow',
22239                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22240             });
22241         }
22242         
22243         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22244     },
22245     
22246     fillMonths: function()
22247     {    
22248         var i = 0;
22249         var months = this.picker().select('>.datepicker-months td', true).first();
22250         
22251         months.dom.innerHTML = '';
22252         
22253         while (i < 12) {
22254             var month = {
22255                 tag: 'span',
22256                 cls: 'month',
22257                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22258             };
22259             
22260             months.createChild(month);
22261         }
22262         
22263     },
22264     
22265     update: function()
22266     {
22267         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;
22268         
22269         if (this.date < this.startDate) {
22270             this.viewDate = new Date(this.startDate);
22271         } else if (this.date > this.endDate) {
22272             this.viewDate = new Date(this.endDate);
22273         } else {
22274             this.viewDate = new Date(this.date);
22275         }
22276         
22277         this.fill();
22278     },
22279     
22280     fill: function() 
22281     {
22282         var d = new Date(this.viewDate),
22283                 year = d.getUTCFullYear(),
22284                 month = d.getUTCMonth(),
22285                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22286                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22287                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22288                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22289                 currentDate = this.date && this.date.valueOf(),
22290                 today = this.UTCToday();
22291         
22292         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22293         
22294 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22295         
22296 //        this.picker.select('>tfoot th.today').
22297 //                                              .text(dates[this.language].today)
22298 //                                              .toggle(this.todayBtn !== false);
22299     
22300         this.updateNavArrows();
22301         this.fillMonths();
22302                                                 
22303         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22304         
22305         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22306          
22307         prevMonth.setUTCDate(day);
22308         
22309         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22310         
22311         var nextMonth = new Date(prevMonth);
22312         
22313         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22314         
22315         nextMonth = nextMonth.valueOf();
22316         
22317         var fillMonths = false;
22318         
22319         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22320         
22321         while(prevMonth.valueOf() <= nextMonth) {
22322             var clsName = '';
22323             
22324             if (prevMonth.getUTCDay() === this.weekStart) {
22325                 if(fillMonths){
22326                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22327                 }
22328                     
22329                 fillMonths = {
22330                     tag: 'tr',
22331                     cn: []
22332                 };
22333                 
22334                 if(this.calendarWeeks){
22335                     // ISO 8601: First week contains first thursday.
22336                     // ISO also states week starts on Monday, but we can be more abstract here.
22337                     var
22338                     // Start of current week: based on weekstart/current date
22339                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22340                     // Thursday of this week
22341                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22342                     // First Thursday of year, year from thursday
22343                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22344                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22345                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22346                     
22347                     fillMonths.cn.push({
22348                         tag: 'td',
22349                         cls: 'cw',
22350                         html: calWeek
22351                     });
22352                 }
22353             }
22354             
22355             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22356                 clsName += ' old';
22357             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22358                 clsName += ' new';
22359             }
22360             if (this.todayHighlight &&
22361                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22362                 prevMonth.getUTCMonth() == today.getMonth() &&
22363                 prevMonth.getUTCDate() == today.getDate()) {
22364                 clsName += ' today';
22365             }
22366             
22367             if (currentDate && prevMonth.valueOf() === currentDate) {
22368                 clsName += ' active';
22369             }
22370             
22371             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22372                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22373                     clsName += ' disabled';
22374             }
22375             
22376             fillMonths.cn.push({
22377                 tag: 'td',
22378                 cls: 'day ' + clsName,
22379                 html: prevMonth.getDate()
22380             });
22381             
22382             prevMonth.setDate(prevMonth.getDate()+1);
22383         }
22384           
22385         var currentYear = this.date && this.date.getUTCFullYear();
22386         var currentMonth = this.date && this.date.getUTCMonth();
22387         
22388         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22389         
22390         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22391             v.removeClass('active');
22392             
22393             if(currentYear === year && k === currentMonth){
22394                 v.addClass('active');
22395             }
22396             
22397             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22398                 v.addClass('disabled');
22399             }
22400             
22401         });
22402         
22403         
22404         year = parseInt(year/10, 10) * 10;
22405         
22406         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22407         
22408         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22409         
22410         year -= 1;
22411         for (var i = -1; i < 11; i++) {
22412             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22413                 tag: 'span',
22414                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22415                 html: year
22416             });
22417             
22418             year += 1;
22419         }
22420     },
22421     
22422     showMode: function(dir) 
22423     {
22424         if (dir) {
22425             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22426         }
22427         
22428         Roo.each(this.picker().select('>div',true).elements, function(v){
22429             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22430             v.hide();
22431         });
22432         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22433     },
22434     
22435     place: function()
22436     {
22437         if(this.isInline) {
22438             return;
22439         }
22440         
22441         this.picker().removeClass(['bottom', 'top']);
22442         
22443         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22444             /*
22445              * place to the top of element!
22446              *
22447              */
22448             
22449             this.picker().addClass('top');
22450             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22451             
22452             return;
22453         }
22454         
22455         this.picker().addClass('bottom');
22456         
22457         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22458     },
22459     
22460     parseDate : function(value)
22461     {
22462         if(!value || value instanceof Date){
22463             return value;
22464         }
22465         var v = Date.parseDate(value, this.format);
22466         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22467             v = Date.parseDate(value, 'Y-m-d');
22468         }
22469         if(!v && this.altFormats){
22470             if(!this.altFormatsArray){
22471                 this.altFormatsArray = this.altFormats.split("|");
22472             }
22473             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22474                 v = Date.parseDate(value, this.altFormatsArray[i]);
22475             }
22476         }
22477         return v;
22478     },
22479     
22480     formatDate : function(date, fmt)
22481     {   
22482         return (!date || !(date instanceof Date)) ?
22483         date : date.dateFormat(fmt || this.format);
22484     },
22485     
22486     onFocus : function()
22487     {
22488         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22489         this.showPopup();
22490     },
22491     
22492     onBlur : function()
22493     {
22494         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22495         
22496         var d = this.inputEl().getValue();
22497         
22498         this.setValue(d);
22499                 
22500         this.hidePopup();
22501     },
22502     
22503     showPopup : function()
22504     {
22505         this.picker().show();
22506         this.update();
22507         this.place();
22508         
22509         this.fireEvent('showpopup', this, this.date);
22510     },
22511     
22512     hidePopup : function()
22513     {
22514         if(this.isInline) {
22515             return;
22516         }
22517         this.picker().hide();
22518         this.viewMode = this.startViewMode;
22519         this.showMode();
22520         
22521         this.fireEvent('hidepopup', this, this.date);
22522         
22523     },
22524     
22525     onMousedown: function(e)
22526     {
22527         e.stopPropagation();
22528         e.preventDefault();
22529     },
22530     
22531     keyup: function(e)
22532     {
22533         Roo.bootstrap.DateField.superclass.keyup.call(this);
22534         this.update();
22535     },
22536
22537     setValue: function(v)
22538     {
22539         if(this.fireEvent('beforeselect', this, v) !== false){
22540             var d = new Date(this.parseDate(v) ).clearTime();
22541         
22542             if(isNaN(d.getTime())){
22543                 this.date = this.viewDate = '';
22544                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22545                 return;
22546             }
22547
22548             v = this.formatDate(d);
22549
22550             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22551
22552             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22553
22554             this.update();
22555
22556             this.fireEvent('select', this, this.date);
22557         }
22558     },
22559     
22560     getValue: function()
22561     {
22562         return this.formatDate(this.date);
22563     },
22564     
22565     fireKey: function(e)
22566     {
22567         if (!this.picker().isVisible()){
22568             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22569                 this.showPopup();
22570             }
22571             return;
22572         }
22573         
22574         var dateChanged = false,
22575         dir, day, month,
22576         newDate, newViewDate;
22577         
22578         switch(e.keyCode){
22579             case 27: // escape
22580                 this.hidePopup();
22581                 e.preventDefault();
22582                 break;
22583             case 37: // left
22584             case 39: // right
22585                 if (!this.keyboardNavigation) {
22586                     break;
22587                 }
22588                 dir = e.keyCode == 37 ? -1 : 1;
22589                 
22590                 if (e.ctrlKey){
22591                     newDate = this.moveYear(this.date, dir);
22592                     newViewDate = this.moveYear(this.viewDate, dir);
22593                 } else if (e.shiftKey){
22594                     newDate = this.moveMonth(this.date, dir);
22595                     newViewDate = this.moveMonth(this.viewDate, dir);
22596                 } else {
22597                     newDate = new Date(this.date);
22598                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22599                     newViewDate = new Date(this.viewDate);
22600                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22601                 }
22602                 if (this.dateWithinRange(newDate)){
22603                     this.date = newDate;
22604                     this.viewDate = newViewDate;
22605                     this.setValue(this.formatDate(this.date));
22606 //                    this.update();
22607                     e.preventDefault();
22608                     dateChanged = true;
22609                 }
22610                 break;
22611             case 38: // up
22612             case 40: // down
22613                 if (!this.keyboardNavigation) {
22614                     break;
22615                 }
22616                 dir = e.keyCode == 38 ? -1 : 1;
22617                 if (e.ctrlKey){
22618                     newDate = this.moveYear(this.date, dir);
22619                     newViewDate = this.moveYear(this.viewDate, dir);
22620                 } else if (e.shiftKey){
22621                     newDate = this.moveMonth(this.date, dir);
22622                     newViewDate = this.moveMonth(this.viewDate, dir);
22623                 } else {
22624                     newDate = new Date(this.date);
22625                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22626                     newViewDate = new Date(this.viewDate);
22627                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22628                 }
22629                 if (this.dateWithinRange(newDate)){
22630                     this.date = newDate;
22631                     this.viewDate = newViewDate;
22632                     this.setValue(this.formatDate(this.date));
22633 //                    this.update();
22634                     e.preventDefault();
22635                     dateChanged = true;
22636                 }
22637                 break;
22638             case 13: // enter
22639                 this.setValue(this.formatDate(this.date));
22640                 this.hidePopup();
22641                 e.preventDefault();
22642                 break;
22643             case 9: // tab
22644                 this.setValue(this.formatDate(this.date));
22645                 this.hidePopup();
22646                 break;
22647             case 16: // shift
22648             case 17: // ctrl
22649             case 18: // alt
22650                 break;
22651             default :
22652                 this.hidePopup();
22653                 
22654         }
22655     },
22656     
22657     
22658     onClick: function(e) 
22659     {
22660         e.stopPropagation();
22661         e.preventDefault();
22662         
22663         var target = e.getTarget();
22664         
22665         if(target.nodeName.toLowerCase() === 'i'){
22666             target = Roo.get(target).dom.parentNode;
22667         }
22668         
22669         var nodeName = target.nodeName;
22670         var className = target.className;
22671         var html = target.innerHTML;
22672         //Roo.log(nodeName);
22673         
22674         switch(nodeName.toLowerCase()) {
22675             case 'th':
22676                 switch(className) {
22677                     case 'switch':
22678                         this.showMode(1);
22679                         break;
22680                     case 'prev':
22681                     case 'next':
22682                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22683                         switch(this.viewMode){
22684                                 case 0:
22685                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22686                                         break;
22687                                 case 1:
22688                                 case 2:
22689                                         this.viewDate = this.moveYear(this.viewDate, dir);
22690                                         break;
22691                         }
22692                         this.fill();
22693                         break;
22694                     case 'today':
22695                         var date = new Date();
22696                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22697 //                        this.fill()
22698                         this.setValue(this.formatDate(this.date));
22699                         
22700                         this.hidePopup();
22701                         break;
22702                 }
22703                 break;
22704             case 'span':
22705                 if (className.indexOf('disabled') < 0) {
22706                 if (!this.viewDate) {
22707                     this.viewDate = new Date();
22708                 }
22709                 this.viewDate.setUTCDate(1);
22710                     if (className.indexOf('month') > -1) {
22711                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22712                     } else {
22713                         var year = parseInt(html, 10) || 0;
22714                         this.viewDate.setUTCFullYear(year);
22715                         
22716                     }
22717                     
22718                     if(this.singleMode){
22719                         this.setValue(this.formatDate(this.viewDate));
22720                         this.hidePopup();
22721                         return;
22722                     }
22723                     
22724                     this.showMode(-1);
22725                     this.fill();
22726                 }
22727                 break;
22728                 
22729             case 'td':
22730                 //Roo.log(className);
22731                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22732                     var day = parseInt(html, 10) || 1;
22733                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
22734                         month = (this.viewDate || new Date()).getUTCMonth();
22735
22736                     if (className.indexOf('old') > -1) {
22737                         if(month === 0 ){
22738                             month = 11;
22739                             year -= 1;
22740                         }else{
22741                             month -= 1;
22742                         }
22743                     } else if (className.indexOf('new') > -1) {
22744                         if (month == 11) {
22745                             month = 0;
22746                             year += 1;
22747                         } else {
22748                             month += 1;
22749                         }
22750                     }
22751                     //Roo.log([year,month,day]);
22752                     this.date = this.UTCDate(year, month, day,0,0,0,0);
22753                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22754 //                    this.fill();
22755                     //Roo.log(this.formatDate(this.date));
22756                     this.setValue(this.formatDate(this.date));
22757                     this.hidePopup();
22758                 }
22759                 break;
22760         }
22761     },
22762     
22763     setStartDate: function(startDate)
22764     {
22765         this.startDate = startDate || -Infinity;
22766         if (this.startDate !== -Infinity) {
22767             this.startDate = this.parseDate(this.startDate);
22768         }
22769         this.update();
22770         this.updateNavArrows();
22771     },
22772
22773     setEndDate: function(endDate)
22774     {
22775         this.endDate = endDate || Infinity;
22776         if (this.endDate !== Infinity) {
22777             this.endDate = this.parseDate(this.endDate);
22778         }
22779         this.update();
22780         this.updateNavArrows();
22781     },
22782     
22783     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22784     {
22785         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22786         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22787             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22788         }
22789         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22790             return parseInt(d, 10);
22791         });
22792         this.update();
22793         this.updateNavArrows();
22794     },
22795     
22796     updateNavArrows: function() 
22797     {
22798         if(this.singleMode){
22799             return;
22800         }
22801         
22802         var d = new Date(this.viewDate),
22803         year = d.getUTCFullYear(),
22804         month = d.getUTCMonth();
22805         
22806         Roo.each(this.picker().select('.prev', true).elements, function(v){
22807             v.show();
22808             switch (this.viewMode) {
22809                 case 0:
22810
22811                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22812                         v.hide();
22813                     }
22814                     break;
22815                 case 1:
22816                 case 2:
22817                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22818                         v.hide();
22819                     }
22820                     break;
22821             }
22822         });
22823         
22824         Roo.each(this.picker().select('.next', true).elements, function(v){
22825             v.show();
22826             switch (this.viewMode) {
22827                 case 0:
22828
22829                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22830                         v.hide();
22831                     }
22832                     break;
22833                 case 1:
22834                 case 2:
22835                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22836                         v.hide();
22837                     }
22838                     break;
22839             }
22840         })
22841     },
22842     
22843     moveMonth: function(date, dir)
22844     {
22845         if (!dir) {
22846             return date;
22847         }
22848         var new_date = new Date(date.valueOf()),
22849         day = new_date.getUTCDate(),
22850         month = new_date.getUTCMonth(),
22851         mag = Math.abs(dir),
22852         new_month, test;
22853         dir = dir > 0 ? 1 : -1;
22854         if (mag == 1){
22855             test = dir == -1
22856             // If going back one month, make sure month is not current month
22857             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22858             ? function(){
22859                 return new_date.getUTCMonth() == month;
22860             }
22861             // If going forward one month, make sure month is as expected
22862             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22863             : function(){
22864                 return new_date.getUTCMonth() != new_month;
22865             };
22866             new_month = month + dir;
22867             new_date.setUTCMonth(new_month);
22868             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22869             if (new_month < 0 || new_month > 11) {
22870                 new_month = (new_month + 12) % 12;
22871             }
22872         } else {
22873             // For magnitudes >1, move one month at a time...
22874             for (var i=0; i<mag; i++) {
22875                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22876                 new_date = this.moveMonth(new_date, dir);
22877             }
22878             // ...then reset the day, keeping it in the new month
22879             new_month = new_date.getUTCMonth();
22880             new_date.setUTCDate(day);
22881             test = function(){
22882                 return new_month != new_date.getUTCMonth();
22883             };
22884         }
22885         // Common date-resetting loop -- if date is beyond end of month, make it
22886         // end of month
22887         while (test()){
22888             new_date.setUTCDate(--day);
22889             new_date.setUTCMonth(new_month);
22890         }
22891         return new_date;
22892     },
22893
22894     moveYear: function(date, dir)
22895     {
22896         return this.moveMonth(date, dir*12);
22897     },
22898
22899     dateWithinRange: function(date)
22900     {
22901         return date >= this.startDate && date <= this.endDate;
22902     },
22903
22904     
22905     remove: function() 
22906     {
22907         this.picker().remove();
22908     },
22909     
22910     validateValue : function(value)
22911     {
22912         if(this.getVisibilityEl().hasClass('hidden')){
22913             return true;
22914         }
22915         
22916         if(value.length < 1)  {
22917             if(this.allowBlank){
22918                 return true;
22919             }
22920             return false;
22921         }
22922         
22923         if(value.length < this.minLength){
22924             return false;
22925         }
22926         if(value.length > this.maxLength){
22927             return false;
22928         }
22929         if(this.vtype){
22930             var vt = Roo.form.VTypes;
22931             if(!vt[this.vtype](value, this)){
22932                 return false;
22933             }
22934         }
22935         if(typeof this.validator == "function"){
22936             var msg = this.validator(value);
22937             if(msg !== true){
22938                 return false;
22939             }
22940         }
22941         
22942         if(this.regex && !this.regex.test(value)){
22943             return false;
22944         }
22945         
22946         if(typeof(this.parseDate(value)) == 'undefined'){
22947             return false;
22948         }
22949         
22950         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22951             return false;
22952         }      
22953         
22954         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22955             return false;
22956         } 
22957         
22958         
22959         return true;
22960     },
22961     
22962     reset : function()
22963     {
22964         this.date = this.viewDate = '';
22965         
22966         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22967     }
22968    
22969 });
22970
22971 Roo.apply(Roo.bootstrap.DateField,  {
22972     
22973     head : {
22974         tag: 'thead',
22975         cn: [
22976         {
22977             tag: 'tr',
22978             cn: [
22979             {
22980                 tag: 'th',
22981                 cls: 'prev',
22982                 html: '<i class="fa fa-arrow-left"/>'
22983             },
22984             {
22985                 tag: 'th',
22986                 cls: 'switch',
22987                 colspan: '5'
22988             },
22989             {
22990                 tag: 'th',
22991                 cls: 'next',
22992                 html: '<i class="fa fa-arrow-right"/>'
22993             }
22994
22995             ]
22996         }
22997         ]
22998     },
22999     
23000     content : {
23001         tag: 'tbody',
23002         cn: [
23003         {
23004             tag: 'tr',
23005             cn: [
23006             {
23007                 tag: 'td',
23008                 colspan: '7'
23009             }
23010             ]
23011         }
23012         ]
23013     },
23014     
23015     footer : {
23016         tag: 'tfoot',
23017         cn: [
23018         {
23019             tag: 'tr',
23020             cn: [
23021             {
23022                 tag: 'th',
23023                 colspan: '7',
23024                 cls: 'today'
23025             }
23026                     
23027             ]
23028         }
23029         ]
23030     },
23031     
23032     dates:{
23033         en: {
23034             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23035             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23036             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23037             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23038             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23039             today: "Today"
23040         }
23041     },
23042     
23043     modes: [
23044     {
23045         clsName: 'days',
23046         navFnc: 'Month',
23047         navStep: 1
23048     },
23049     {
23050         clsName: 'months',
23051         navFnc: 'FullYear',
23052         navStep: 1
23053     },
23054     {
23055         clsName: 'years',
23056         navFnc: 'FullYear',
23057         navStep: 10
23058     }]
23059 });
23060
23061 Roo.apply(Roo.bootstrap.DateField,  {
23062   
23063     template : {
23064         tag: 'div',
23065         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23066         cn: [
23067         {
23068             tag: 'div',
23069             cls: 'datepicker-days',
23070             cn: [
23071             {
23072                 tag: 'table',
23073                 cls: 'table-condensed',
23074                 cn:[
23075                 Roo.bootstrap.DateField.head,
23076                 {
23077                     tag: 'tbody'
23078                 },
23079                 Roo.bootstrap.DateField.footer
23080                 ]
23081             }
23082             ]
23083         },
23084         {
23085             tag: 'div',
23086             cls: 'datepicker-months',
23087             cn: [
23088             {
23089                 tag: 'table',
23090                 cls: 'table-condensed',
23091                 cn:[
23092                 Roo.bootstrap.DateField.head,
23093                 Roo.bootstrap.DateField.content,
23094                 Roo.bootstrap.DateField.footer
23095                 ]
23096             }
23097             ]
23098         },
23099         {
23100             tag: 'div',
23101             cls: 'datepicker-years',
23102             cn: [
23103             {
23104                 tag: 'table',
23105                 cls: 'table-condensed',
23106                 cn:[
23107                 Roo.bootstrap.DateField.head,
23108                 Roo.bootstrap.DateField.content,
23109                 Roo.bootstrap.DateField.footer
23110                 ]
23111             }
23112             ]
23113         }
23114         ]
23115     }
23116 });
23117
23118  
23119
23120  /*
23121  * - LGPL
23122  *
23123  * TimeField
23124  * 
23125  */
23126
23127 /**
23128  * @class Roo.bootstrap.TimeField
23129  * @extends Roo.bootstrap.Input
23130  * Bootstrap DateField class
23131  * 
23132  * 
23133  * @constructor
23134  * Create a new TimeField
23135  * @param {Object} config The config object
23136  */
23137
23138 Roo.bootstrap.TimeField = function(config){
23139     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23140     this.addEvents({
23141             /**
23142              * @event show
23143              * Fires when this field show.
23144              * @param {Roo.bootstrap.DateField} thisthis
23145              * @param {Mixed} date The date value
23146              */
23147             show : true,
23148             /**
23149              * @event show
23150              * Fires when this field hide.
23151              * @param {Roo.bootstrap.DateField} this
23152              * @param {Mixed} date The date value
23153              */
23154             hide : true,
23155             /**
23156              * @event select
23157              * Fires when select a date.
23158              * @param {Roo.bootstrap.DateField} this
23159              * @param {Mixed} date The date value
23160              */
23161             select : true
23162         });
23163 };
23164
23165 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23166     
23167     /**
23168      * @cfg {String} format
23169      * The default time format string which can be overriden for localization support.  The format must be
23170      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23171      */
23172     format : "H:i",
23173
23174     getAutoCreate : function()
23175     {
23176         this.after = '<i class="fa far fa-clock"></i>';
23177         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23178         
23179          
23180     },
23181     onRender: function(ct, position)
23182     {
23183         
23184         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23185                 
23186         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23187         
23188         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23189         
23190         this.pop = this.picker().select('>.datepicker-time',true).first();
23191         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23192         
23193         this.picker().on('mousedown', this.onMousedown, this);
23194         this.picker().on('click', this.onClick, this);
23195         
23196         this.picker().addClass('datepicker-dropdown');
23197     
23198         this.fillTime();
23199         this.update();
23200             
23201         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23202         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23203         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23204         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23205         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23206         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23207
23208     },
23209     
23210     fireKey: function(e){
23211         if (!this.picker().isVisible()){
23212             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23213                 this.show();
23214             }
23215             return;
23216         }
23217
23218         e.preventDefault();
23219         
23220         switch(e.keyCode){
23221             case 27: // escape
23222                 this.hide();
23223                 break;
23224             case 37: // left
23225             case 39: // right
23226                 this.onTogglePeriod();
23227                 break;
23228             case 38: // up
23229                 this.onIncrementMinutes();
23230                 break;
23231             case 40: // down
23232                 this.onDecrementMinutes();
23233                 break;
23234             case 13: // enter
23235             case 9: // tab
23236                 this.setTime();
23237                 break;
23238         }
23239     },
23240     
23241     onClick: function(e) {
23242         e.stopPropagation();
23243         e.preventDefault();
23244     },
23245     
23246     picker : function()
23247     {
23248         return this.pickerEl;
23249     },
23250     
23251     fillTime: function()
23252     {    
23253         var time = this.pop.select('tbody', true).first();
23254         
23255         time.dom.innerHTML = '';
23256         
23257         time.createChild({
23258             tag: 'tr',
23259             cn: [
23260                 {
23261                     tag: 'td',
23262                     cn: [
23263                         {
23264                             tag: 'a',
23265                             href: '#',
23266                             cls: 'btn',
23267                             cn: [
23268                                 {
23269                                     tag: 'i',
23270                                     cls: 'hours-up fa fas fa-chevron-up'
23271                                 }
23272                             ]
23273                         } 
23274                     ]
23275                 },
23276                 {
23277                     tag: 'td',
23278                     cls: 'separator'
23279                 },
23280                 {
23281                     tag: 'td',
23282                     cn: [
23283                         {
23284                             tag: 'a',
23285                             href: '#',
23286                             cls: 'btn',
23287                             cn: [
23288                                 {
23289                                     tag: 'i',
23290                                     cls: 'minutes-up fa fas fa-chevron-up'
23291                                 }
23292                             ]
23293                         }
23294                     ]
23295                 },
23296                 {
23297                     tag: 'td',
23298                     cls: 'separator'
23299                 }
23300             ]
23301         });
23302         
23303         time.createChild({
23304             tag: 'tr',
23305             cn: [
23306                 {
23307                     tag: 'td',
23308                     cn: [
23309                         {
23310                             tag: 'span',
23311                             cls: 'timepicker-hour',
23312                             html: '00'
23313                         }  
23314                     ]
23315                 },
23316                 {
23317                     tag: 'td',
23318                     cls: 'separator',
23319                     html: ':'
23320                 },
23321                 {
23322                     tag: 'td',
23323                     cn: [
23324                         {
23325                             tag: 'span',
23326                             cls: 'timepicker-minute',
23327                             html: '00'
23328                         }  
23329                     ]
23330                 },
23331                 {
23332                     tag: 'td',
23333                     cls: 'separator'
23334                 },
23335                 {
23336                     tag: 'td',
23337                     cn: [
23338                         {
23339                             tag: 'button',
23340                             type: 'button',
23341                             cls: 'btn btn-primary period',
23342                             html: 'AM'
23343                             
23344                         }
23345                     ]
23346                 }
23347             ]
23348         });
23349         
23350         time.createChild({
23351             tag: 'tr',
23352             cn: [
23353                 {
23354                     tag: 'td',
23355                     cn: [
23356                         {
23357                             tag: 'a',
23358                             href: '#',
23359                             cls: 'btn',
23360                             cn: [
23361                                 {
23362                                     tag: 'span',
23363                                     cls: 'hours-down fa fas fa-chevron-down'
23364                                 }
23365                             ]
23366                         }
23367                     ]
23368                 },
23369                 {
23370                     tag: 'td',
23371                     cls: 'separator'
23372                 },
23373                 {
23374                     tag: 'td',
23375                     cn: [
23376                         {
23377                             tag: 'a',
23378                             href: '#',
23379                             cls: 'btn',
23380                             cn: [
23381                                 {
23382                                     tag: 'span',
23383                                     cls: 'minutes-down fa fas fa-chevron-down'
23384                                 }
23385                             ]
23386                         }
23387                     ]
23388                 },
23389                 {
23390                     tag: 'td',
23391                     cls: 'separator'
23392                 }
23393             ]
23394         });
23395         
23396     },
23397     
23398     update: function()
23399     {
23400         
23401         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23402         
23403         this.fill();
23404     },
23405     
23406     fill: function() 
23407     {
23408         var hours = this.time.getHours();
23409         var minutes = this.time.getMinutes();
23410         var period = 'AM';
23411         
23412         if(hours > 11){
23413             period = 'PM';
23414         }
23415         
23416         if(hours == 0){
23417             hours = 12;
23418         }
23419         
23420         
23421         if(hours > 12){
23422             hours = hours - 12;
23423         }
23424         
23425         if(hours < 10){
23426             hours = '0' + hours;
23427         }
23428         
23429         if(minutes < 10){
23430             minutes = '0' + minutes;
23431         }
23432         
23433         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23434         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23435         this.pop.select('button', true).first().dom.innerHTML = period;
23436         
23437     },
23438     
23439     place: function()
23440     {   
23441         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23442         
23443         var cls = ['bottom'];
23444         
23445         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23446             cls.pop();
23447             cls.push('top');
23448         }
23449         
23450         cls.push('right');
23451         
23452         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23453             cls.pop();
23454             cls.push('left');
23455         }
23456         //this.picker().setXY(20000,20000);
23457         this.picker().addClass(cls.join('-'));
23458         
23459         var _this = this;
23460         
23461         Roo.each(cls, function(c){
23462             if(c == 'bottom'){
23463                 (function() {
23464                  //  
23465                 }).defer(200);
23466                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23467                 //_this.picker().setTop(_this.inputEl().getHeight());
23468                 return;
23469             }
23470             if(c == 'top'){
23471                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23472                 
23473                 //_this.picker().setTop(0 - _this.picker().getHeight());
23474                 return;
23475             }
23476             /*
23477             if(c == 'left'){
23478                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23479                 return;
23480             }
23481             if(c == 'right'){
23482                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23483                 return;
23484             }
23485             */
23486         });
23487         
23488     },
23489   
23490     onFocus : function()
23491     {
23492         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23493         this.show();
23494     },
23495     
23496     onBlur : function()
23497     {
23498         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23499         this.hide();
23500     },
23501     
23502     show : function()
23503     {
23504         this.picker().show();
23505         this.pop.show();
23506         this.update();
23507         this.place();
23508         
23509         this.fireEvent('show', this, this.date);
23510     },
23511     
23512     hide : function()
23513     {
23514         this.picker().hide();
23515         this.pop.hide();
23516         
23517         this.fireEvent('hide', this, this.date);
23518     },
23519     
23520     setTime : function()
23521     {
23522         this.hide();
23523         this.setValue(this.time.format(this.format));
23524         
23525         this.fireEvent('select', this, this.date);
23526         
23527         
23528     },
23529     
23530     onMousedown: function(e){
23531         e.stopPropagation();
23532         e.preventDefault();
23533     },
23534     
23535     onIncrementHours: function()
23536     {
23537         Roo.log('onIncrementHours');
23538         this.time = this.time.add(Date.HOUR, 1);
23539         this.update();
23540         
23541     },
23542     
23543     onDecrementHours: function()
23544     {
23545         Roo.log('onDecrementHours');
23546         this.time = this.time.add(Date.HOUR, -1);
23547         this.update();
23548     },
23549     
23550     onIncrementMinutes: function()
23551     {
23552         Roo.log('onIncrementMinutes');
23553         this.time = this.time.add(Date.MINUTE, 1);
23554         this.update();
23555     },
23556     
23557     onDecrementMinutes: function()
23558     {
23559         Roo.log('onDecrementMinutes');
23560         this.time = this.time.add(Date.MINUTE, -1);
23561         this.update();
23562     },
23563     
23564     onTogglePeriod: function()
23565     {
23566         Roo.log('onTogglePeriod');
23567         this.time = this.time.add(Date.HOUR, 12);
23568         this.update();
23569     }
23570     
23571    
23572 });
23573  
23574
23575 Roo.apply(Roo.bootstrap.TimeField,  {
23576   
23577     template : {
23578         tag: 'div',
23579         cls: 'datepicker dropdown-menu',
23580         cn: [
23581             {
23582                 tag: 'div',
23583                 cls: 'datepicker-time',
23584                 cn: [
23585                 {
23586                     tag: 'table',
23587                     cls: 'table-condensed',
23588                     cn:[
23589                         {
23590                             tag: 'tbody',
23591                             cn: [
23592                                 {
23593                                     tag: 'tr',
23594                                     cn: [
23595                                     {
23596                                         tag: 'td',
23597                                         colspan: '7'
23598                                     }
23599                                     ]
23600                                 }
23601                             ]
23602                         },
23603                         {
23604                             tag: 'tfoot',
23605                             cn: [
23606                                 {
23607                                     tag: 'tr',
23608                                     cn: [
23609                                     {
23610                                         tag: 'th',
23611                                         colspan: '7',
23612                                         cls: '',
23613                                         cn: [
23614                                             {
23615                                                 tag: 'button',
23616                                                 cls: 'btn btn-info ok',
23617                                                 html: 'OK'
23618                                             }
23619                                         ]
23620                                     }
23621                     
23622                                     ]
23623                                 }
23624                             ]
23625                         }
23626                     ]
23627                 }
23628                 ]
23629             }
23630         ]
23631     }
23632 });
23633
23634  
23635
23636  /*
23637  * - LGPL
23638  *
23639  * MonthField
23640  * 
23641  */
23642
23643 /**
23644  * @class Roo.bootstrap.MonthField
23645  * @extends Roo.bootstrap.Input
23646  * Bootstrap MonthField class
23647  * 
23648  * @cfg {String} language default en
23649  * 
23650  * @constructor
23651  * Create a new MonthField
23652  * @param {Object} config The config object
23653  */
23654
23655 Roo.bootstrap.MonthField = function(config){
23656     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23657     
23658     this.addEvents({
23659         /**
23660          * @event show
23661          * Fires when this field show.
23662          * @param {Roo.bootstrap.MonthField} this
23663          * @param {Mixed} date The date value
23664          */
23665         show : true,
23666         /**
23667          * @event show
23668          * Fires when this field hide.
23669          * @param {Roo.bootstrap.MonthField} this
23670          * @param {Mixed} date The date value
23671          */
23672         hide : true,
23673         /**
23674          * @event select
23675          * Fires when select a date.
23676          * @param {Roo.bootstrap.MonthField} this
23677          * @param {String} oldvalue The old value
23678          * @param {String} newvalue The new value
23679          */
23680         select : true
23681     });
23682 };
23683
23684 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23685     
23686     onRender: function(ct, position)
23687     {
23688         
23689         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23690         
23691         this.language = this.language || 'en';
23692         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23693         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23694         
23695         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23696         this.isInline = false;
23697         this.isInput = true;
23698         this.component = this.el.select('.add-on', true).first() || false;
23699         this.component = (this.component && this.component.length === 0) ? false : this.component;
23700         this.hasInput = this.component && this.inputEL().length;
23701         
23702         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23703         
23704         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23705         
23706         this.picker().on('mousedown', this.onMousedown, this);
23707         this.picker().on('click', this.onClick, this);
23708         
23709         this.picker().addClass('datepicker-dropdown');
23710         
23711         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23712             v.setStyle('width', '189px');
23713         });
23714         
23715         this.fillMonths();
23716         
23717         this.update();
23718         
23719         if(this.isInline) {
23720             this.show();
23721         }
23722         
23723     },
23724     
23725     setValue: function(v, suppressEvent)
23726     {   
23727         var o = this.getValue();
23728         
23729         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23730         
23731         this.update();
23732
23733         if(suppressEvent !== true){
23734             this.fireEvent('select', this, o, v);
23735         }
23736         
23737     },
23738     
23739     getValue: function()
23740     {
23741         return this.value;
23742     },
23743     
23744     onClick: function(e) 
23745     {
23746         e.stopPropagation();
23747         e.preventDefault();
23748         
23749         var target = e.getTarget();
23750         
23751         if(target.nodeName.toLowerCase() === 'i'){
23752             target = Roo.get(target).dom.parentNode;
23753         }
23754         
23755         var nodeName = target.nodeName;
23756         var className = target.className;
23757         var html = target.innerHTML;
23758         
23759         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23760             return;
23761         }
23762         
23763         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23764         
23765         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23766         
23767         this.hide();
23768                         
23769     },
23770     
23771     picker : function()
23772     {
23773         return this.pickerEl;
23774     },
23775     
23776     fillMonths: function()
23777     {    
23778         var i = 0;
23779         var months = this.picker().select('>.datepicker-months td', true).first();
23780         
23781         months.dom.innerHTML = '';
23782         
23783         while (i < 12) {
23784             var month = {
23785                 tag: 'span',
23786                 cls: 'month',
23787                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23788             };
23789             
23790             months.createChild(month);
23791         }
23792         
23793     },
23794     
23795     update: function()
23796     {
23797         var _this = this;
23798         
23799         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23800             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23801         }
23802         
23803         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23804             e.removeClass('active');
23805             
23806             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23807                 e.addClass('active');
23808             }
23809         })
23810     },
23811     
23812     place: function()
23813     {
23814         if(this.isInline) {
23815             return;
23816         }
23817         
23818         this.picker().removeClass(['bottom', 'top']);
23819         
23820         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23821             /*
23822              * place to the top of element!
23823              *
23824              */
23825             
23826             this.picker().addClass('top');
23827             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23828             
23829             return;
23830         }
23831         
23832         this.picker().addClass('bottom');
23833         
23834         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23835     },
23836     
23837     onFocus : function()
23838     {
23839         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23840         this.show();
23841     },
23842     
23843     onBlur : function()
23844     {
23845         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23846         
23847         var d = this.inputEl().getValue();
23848         
23849         this.setValue(d);
23850                 
23851         this.hide();
23852     },
23853     
23854     show : function()
23855     {
23856         this.picker().show();
23857         this.picker().select('>.datepicker-months', true).first().show();
23858         this.update();
23859         this.place();
23860         
23861         this.fireEvent('show', this, this.date);
23862     },
23863     
23864     hide : function()
23865     {
23866         if(this.isInline) {
23867             return;
23868         }
23869         this.picker().hide();
23870         this.fireEvent('hide', this, this.date);
23871         
23872     },
23873     
23874     onMousedown: function(e)
23875     {
23876         e.stopPropagation();
23877         e.preventDefault();
23878     },
23879     
23880     keyup: function(e)
23881     {
23882         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23883         this.update();
23884     },
23885
23886     fireKey: function(e)
23887     {
23888         if (!this.picker().isVisible()){
23889             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23890                 this.show();
23891             }
23892             return;
23893         }
23894         
23895         var dir;
23896         
23897         switch(e.keyCode){
23898             case 27: // escape
23899                 this.hide();
23900                 e.preventDefault();
23901                 break;
23902             case 37: // left
23903             case 39: // right
23904                 dir = e.keyCode == 37 ? -1 : 1;
23905                 
23906                 this.vIndex = this.vIndex + dir;
23907                 
23908                 if(this.vIndex < 0){
23909                     this.vIndex = 0;
23910                 }
23911                 
23912                 if(this.vIndex > 11){
23913                     this.vIndex = 11;
23914                 }
23915                 
23916                 if(isNaN(this.vIndex)){
23917                     this.vIndex = 0;
23918                 }
23919                 
23920                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23921                 
23922                 break;
23923             case 38: // up
23924             case 40: // down
23925                 
23926                 dir = e.keyCode == 38 ? -1 : 1;
23927                 
23928                 this.vIndex = this.vIndex + dir * 4;
23929                 
23930                 if(this.vIndex < 0){
23931                     this.vIndex = 0;
23932                 }
23933                 
23934                 if(this.vIndex > 11){
23935                     this.vIndex = 11;
23936                 }
23937                 
23938                 if(isNaN(this.vIndex)){
23939                     this.vIndex = 0;
23940                 }
23941                 
23942                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23943                 break;
23944                 
23945             case 13: // enter
23946                 
23947                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23948                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23949                 }
23950                 
23951                 this.hide();
23952                 e.preventDefault();
23953                 break;
23954             case 9: // tab
23955                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23956                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23957                 }
23958                 this.hide();
23959                 break;
23960             case 16: // shift
23961             case 17: // ctrl
23962             case 18: // alt
23963                 break;
23964             default :
23965                 this.hide();
23966                 
23967         }
23968     },
23969     
23970     remove: function() 
23971     {
23972         this.picker().remove();
23973     }
23974    
23975 });
23976
23977 Roo.apply(Roo.bootstrap.MonthField,  {
23978     
23979     content : {
23980         tag: 'tbody',
23981         cn: [
23982         {
23983             tag: 'tr',
23984             cn: [
23985             {
23986                 tag: 'td',
23987                 colspan: '7'
23988             }
23989             ]
23990         }
23991         ]
23992     },
23993     
23994     dates:{
23995         en: {
23996             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23997             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23998         }
23999     }
24000 });
24001
24002 Roo.apply(Roo.bootstrap.MonthField,  {
24003   
24004     template : {
24005         tag: 'div',
24006         cls: 'datepicker dropdown-menu roo-dynamic',
24007         cn: [
24008             {
24009                 tag: 'div',
24010                 cls: 'datepicker-months',
24011                 cn: [
24012                 {
24013                     tag: 'table',
24014                     cls: 'table-condensed',
24015                     cn:[
24016                         Roo.bootstrap.DateField.content
24017                     ]
24018                 }
24019                 ]
24020             }
24021         ]
24022     }
24023 });
24024
24025  
24026
24027  
24028  /*
24029  * - LGPL
24030  *
24031  * CheckBox
24032  * 
24033  */
24034
24035 /**
24036  * @class Roo.bootstrap.CheckBox
24037  * @extends Roo.bootstrap.Input
24038  * Bootstrap CheckBox class
24039  * 
24040  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24041  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24042  * @cfg {String} boxLabel The text that appears beside the checkbox
24043  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24044  * @cfg {Boolean} checked initnal the element
24045  * @cfg {Boolean} inline inline the element (default false)
24046  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24047  * @cfg {String} tooltip label tooltip
24048  * 
24049  * @constructor
24050  * Create a new CheckBox
24051  * @param {Object} config The config object
24052  */
24053
24054 Roo.bootstrap.CheckBox = function(config){
24055     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24056    
24057     this.addEvents({
24058         /**
24059         * @event check
24060         * Fires when the element is checked or unchecked.
24061         * @param {Roo.bootstrap.CheckBox} this This input
24062         * @param {Boolean} checked The new checked value
24063         */
24064        check : true,
24065        /**
24066         * @event click
24067         * Fires when the element is click.
24068         * @param {Roo.bootstrap.CheckBox} this This input
24069         */
24070        click : true
24071     });
24072     
24073 };
24074
24075 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24076   
24077     inputType: 'checkbox',
24078     inputValue: 1,
24079     valueOff: 0,
24080     boxLabel: false,
24081     checked: false,
24082     weight : false,
24083     inline: false,
24084     tooltip : '',
24085     
24086     // checkbox success does not make any sense really.. 
24087     invalidClass : "",
24088     validClass : "",
24089     
24090     
24091     getAutoCreate : function()
24092     {
24093         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24094         
24095         var id = Roo.id();
24096         
24097         var cfg = {};
24098         
24099         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24100         
24101         if(this.inline){
24102             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24103         }
24104         
24105         var input =  {
24106             tag: 'input',
24107             id : id,
24108             type : this.inputType,
24109             value : this.inputValue,
24110             cls : 'roo-' + this.inputType, //'form-box',
24111             placeholder : this.placeholder || ''
24112             
24113         };
24114         
24115         if(this.inputType != 'radio'){
24116             var hidden =  {
24117                 tag: 'input',
24118                 type : 'hidden',
24119                 cls : 'roo-hidden-value',
24120                 value : this.checked ? this.inputValue : this.valueOff
24121             };
24122         }
24123         
24124             
24125         if (this.weight) { // Validity check?
24126             cfg.cls += " " + this.inputType + "-" + this.weight;
24127         }
24128         
24129         if (this.disabled) {
24130             input.disabled=true;
24131         }
24132         
24133         if(this.checked){
24134             input.checked = this.checked;
24135         }
24136         
24137         if (this.name) {
24138             
24139             input.name = this.name;
24140             
24141             if(this.inputType != 'radio'){
24142                 hidden.name = this.name;
24143                 input.name = '_hidden_' + this.name;
24144             }
24145         }
24146         
24147         if (this.size) {
24148             input.cls += ' input-' + this.size;
24149         }
24150         
24151         var settings=this;
24152         
24153         ['xs','sm','md','lg'].map(function(size){
24154             if (settings[size]) {
24155                 cfg.cls += ' col-' + size + '-' + settings[size];
24156             }
24157         });
24158         
24159         var inputblock = input;
24160          
24161         if (this.before || this.after) {
24162             
24163             inputblock = {
24164                 cls : 'input-group',
24165                 cn :  [] 
24166             };
24167             
24168             if (this.before) {
24169                 inputblock.cn.push({
24170                     tag :'span',
24171                     cls : 'input-group-addon',
24172                     html : this.before
24173                 });
24174             }
24175             
24176             inputblock.cn.push(input);
24177             
24178             if(this.inputType != 'radio'){
24179                 inputblock.cn.push(hidden);
24180             }
24181             
24182             if (this.after) {
24183                 inputblock.cn.push({
24184                     tag :'span',
24185                     cls : 'input-group-addon',
24186                     html : this.after
24187                 });
24188             }
24189             
24190         }
24191         var boxLabelCfg = false;
24192         
24193         if(this.boxLabel){
24194            
24195             boxLabelCfg = {
24196                 tag: 'label',
24197                 //'for': id, // box label is handled by onclick - so no for...
24198                 cls: 'box-label',
24199                 html: this.boxLabel
24200             };
24201             if(this.tooltip){
24202                 boxLabelCfg.tooltip = this.tooltip;
24203             }
24204              
24205         }
24206         
24207         
24208         if (align ==='left' && this.fieldLabel.length) {
24209 //                Roo.log("left and has label");
24210             cfg.cn = [
24211                 {
24212                     tag: 'label',
24213                     'for' :  id,
24214                     cls : 'control-label',
24215                     html : this.fieldLabel
24216                 },
24217                 {
24218                     cls : "", 
24219                     cn: [
24220                         inputblock
24221                     ]
24222                 }
24223             ];
24224             
24225             if (boxLabelCfg) {
24226                 cfg.cn[1].cn.push(boxLabelCfg);
24227             }
24228             
24229             if(this.labelWidth > 12){
24230                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24231             }
24232             
24233             if(this.labelWidth < 13 && this.labelmd == 0){
24234                 this.labelmd = this.labelWidth;
24235             }
24236             
24237             if(this.labellg > 0){
24238                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24239                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24240             }
24241             
24242             if(this.labelmd > 0){
24243                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24244                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24245             }
24246             
24247             if(this.labelsm > 0){
24248                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24249                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24250             }
24251             
24252             if(this.labelxs > 0){
24253                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24254                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24255             }
24256             
24257         } else if ( this.fieldLabel.length) {
24258 //                Roo.log(" label");
24259                 cfg.cn = [
24260                    
24261                     {
24262                         tag: this.boxLabel ? 'span' : 'label',
24263                         'for': id,
24264                         cls: 'control-label box-input-label',
24265                         //cls : 'input-group-addon',
24266                         html : this.fieldLabel
24267                     },
24268                     
24269                     inputblock
24270                     
24271                 ];
24272                 if (boxLabelCfg) {
24273                     cfg.cn.push(boxLabelCfg);
24274                 }
24275
24276         } else {
24277             
24278 //                Roo.log(" no label && no align");
24279                 cfg.cn = [  inputblock ] ;
24280                 if (boxLabelCfg) {
24281                     cfg.cn.push(boxLabelCfg);
24282                 }
24283
24284                 
24285         }
24286         
24287        
24288         
24289         if(this.inputType != 'radio'){
24290             cfg.cn.push(hidden);
24291         }
24292         
24293         return cfg;
24294         
24295     },
24296     
24297     /**
24298      * return the real input element.
24299      */
24300     inputEl: function ()
24301     {
24302         return this.el.select('input.roo-' + this.inputType,true).first();
24303     },
24304     hiddenEl: function ()
24305     {
24306         return this.el.select('input.roo-hidden-value',true).first();
24307     },
24308     
24309     labelEl: function()
24310     {
24311         return this.el.select('label.control-label',true).first();
24312     },
24313     /* depricated... */
24314     
24315     label: function()
24316     {
24317         return this.labelEl();
24318     },
24319     
24320     boxLabelEl: function()
24321     {
24322         return this.el.select('label.box-label',true).first();
24323     },
24324     
24325     initEvents : function()
24326     {
24327 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24328         
24329         this.inputEl().on('click', this.onClick,  this);
24330         
24331         if (this.boxLabel) { 
24332             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24333         }
24334         
24335         this.startValue = this.getValue();
24336         
24337         if(this.groupId){
24338             Roo.bootstrap.CheckBox.register(this);
24339         }
24340     },
24341     
24342     onClick : function(e)
24343     {   
24344         if(this.fireEvent('click', this, e) !== false){
24345             this.setChecked(!this.checked);
24346         }
24347         
24348     },
24349     
24350     setChecked : function(state,suppressEvent)
24351     {
24352         this.startValue = this.getValue();
24353
24354         if(this.inputType == 'radio'){
24355             
24356             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24357                 e.dom.checked = false;
24358             });
24359             
24360             this.inputEl().dom.checked = true;
24361             
24362             this.inputEl().dom.value = this.inputValue;
24363             
24364             if(suppressEvent !== true){
24365                 this.fireEvent('check', this, true);
24366             }
24367             
24368             this.validate();
24369             
24370             return;
24371         }
24372         
24373         this.checked = state;
24374         
24375         this.inputEl().dom.checked = state;
24376         
24377         
24378         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24379         
24380         if(suppressEvent !== true){
24381             this.fireEvent('check', this, state);
24382         }
24383         
24384         this.validate();
24385     },
24386     
24387     getValue : function()
24388     {
24389         if(this.inputType == 'radio'){
24390             return this.getGroupValue();
24391         }
24392         
24393         return this.hiddenEl().dom.value;
24394         
24395     },
24396     
24397     getGroupValue : function()
24398     {
24399         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24400             return '';
24401         }
24402         
24403         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24404     },
24405     
24406     setValue : function(v,suppressEvent)
24407     {
24408         if(this.inputType == 'radio'){
24409             this.setGroupValue(v, suppressEvent);
24410             return;
24411         }
24412         
24413         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24414         
24415         this.validate();
24416     },
24417     
24418     setGroupValue : function(v, suppressEvent)
24419     {
24420         this.startValue = this.getValue();
24421         
24422         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24423             e.dom.checked = false;
24424             
24425             if(e.dom.value == v){
24426                 e.dom.checked = true;
24427             }
24428         });
24429         
24430         if(suppressEvent !== true){
24431             this.fireEvent('check', this, true);
24432         }
24433
24434         this.validate();
24435         
24436         return;
24437     },
24438     
24439     validate : function()
24440     {
24441         if(this.getVisibilityEl().hasClass('hidden')){
24442             return true;
24443         }
24444         
24445         if(
24446                 this.disabled || 
24447                 (this.inputType == 'radio' && this.validateRadio()) ||
24448                 (this.inputType == 'checkbox' && this.validateCheckbox())
24449         ){
24450             this.markValid();
24451             return true;
24452         }
24453         
24454         this.markInvalid();
24455         return false;
24456     },
24457     
24458     validateRadio : function()
24459     {
24460         if(this.getVisibilityEl().hasClass('hidden')){
24461             return true;
24462         }
24463         
24464         if(this.allowBlank){
24465             return true;
24466         }
24467         
24468         var valid = false;
24469         
24470         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24471             if(!e.dom.checked){
24472                 return;
24473             }
24474             
24475             valid = true;
24476             
24477             return false;
24478         });
24479         
24480         return valid;
24481     },
24482     
24483     validateCheckbox : function()
24484     {
24485         if(!this.groupId){
24486             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24487             //return (this.getValue() == this.inputValue) ? true : false;
24488         }
24489         
24490         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24491         
24492         if(!group){
24493             return false;
24494         }
24495         
24496         var r = false;
24497         
24498         for(var i in group){
24499             if(group[i].el.isVisible(true)){
24500                 r = false;
24501                 break;
24502             }
24503             
24504             r = true;
24505         }
24506         
24507         for(var i in group){
24508             if(r){
24509                 break;
24510             }
24511             
24512             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24513         }
24514         
24515         return r;
24516     },
24517     
24518     /**
24519      * Mark this field as valid
24520      */
24521     markValid : function()
24522     {
24523         var _this = this;
24524         
24525         this.fireEvent('valid', this);
24526         
24527         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24528         
24529         if(this.groupId){
24530             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24531         }
24532         
24533         if(label){
24534             label.markValid();
24535         }
24536
24537         if(this.inputType == 'radio'){
24538             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24539                 var fg = e.findParent('.form-group', false, true);
24540                 if (Roo.bootstrap.version == 3) {
24541                     fg.removeClass([_this.invalidClass, _this.validClass]);
24542                     fg.addClass(_this.validClass);
24543                 } else {
24544                     fg.removeClass(['is-valid', 'is-invalid']);
24545                     fg.addClass('is-valid');
24546                 }
24547             });
24548             
24549             return;
24550         }
24551
24552         if(!this.groupId){
24553             var fg = this.el.findParent('.form-group', false, true);
24554             if (Roo.bootstrap.version == 3) {
24555                 fg.removeClass([this.invalidClass, this.validClass]);
24556                 fg.addClass(this.validClass);
24557             } else {
24558                 fg.removeClass(['is-valid', 'is-invalid']);
24559                 fg.addClass('is-valid');
24560             }
24561             return;
24562         }
24563         
24564         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24565         
24566         if(!group){
24567             return;
24568         }
24569         
24570         for(var i in group){
24571             var fg = group[i].el.findParent('.form-group', false, true);
24572             if (Roo.bootstrap.version == 3) {
24573                 fg.removeClass([this.invalidClass, this.validClass]);
24574                 fg.addClass(this.validClass);
24575             } else {
24576                 fg.removeClass(['is-valid', 'is-invalid']);
24577                 fg.addClass('is-valid');
24578             }
24579         }
24580     },
24581     
24582      /**
24583      * Mark this field as invalid
24584      * @param {String} msg The validation message
24585      */
24586     markInvalid : function(msg)
24587     {
24588         if(this.allowBlank){
24589             return;
24590         }
24591         
24592         var _this = this;
24593         
24594         this.fireEvent('invalid', this, msg);
24595         
24596         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24597         
24598         if(this.groupId){
24599             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24600         }
24601         
24602         if(label){
24603             label.markInvalid();
24604         }
24605             
24606         if(this.inputType == 'radio'){
24607             
24608             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24609                 var fg = e.findParent('.form-group', false, true);
24610                 if (Roo.bootstrap.version == 3) {
24611                     fg.removeClass([_this.invalidClass, _this.validClass]);
24612                     fg.addClass(_this.invalidClass);
24613                 } else {
24614                     fg.removeClass(['is-invalid', 'is-valid']);
24615                     fg.addClass('is-invalid');
24616                 }
24617             });
24618             
24619             return;
24620         }
24621         
24622         if(!this.groupId){
24623             var fg = this.el.findParent('.form-group', false, true);
24624             if (Roo.bootstrap.version == 3) {
24625                 fg.removeClass([_this.invalidClass, _this.validClass]);
24626                 fg.addClass(_this.invalidClass);
24627             } else {
24628                 fg.removeClass(['is-invalid', 'is-valid']);
24629                 fg.addClass('is-invalid');
24630             }
24631             return;
24632         }
24633         
24634         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24635         
24636         if(!group){
24637             return;
24638         }
24639         
24640         for(var i in group){
24641             var fg = group[i].el.findParent('.form-group', false, true);
24642             if (Roo.bootstrap.version == 3) {
24643                 fg.removeClass([_this.invalidClass, _this.validClass]);
24644                 fg.addClass(_this.invalidClass);
24645             } else {
24646                 fg.removeClass(['is-invalid', 'is-valid']);
24647                 fg.addClass('is-invalid');
24648             }
24649         }
24650         
24651     },
24652     
24653     clearInvalid : function()
24654     {
24655         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24656         
24657         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24658         
24659         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24660         
24661         if (label && label.iconEl) {
24662             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24663             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24664         }
24665     },
24666     
24667     disable : function()
24668     {
24669         if(this.inputType != 'radio'){
24670             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24671             return;
24672         }
24673         
24674         var _this = this;
24675         
24676         if(this.rendered){
24677             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24678                 _this.getActionEl().addClass(this.disabledClass);
24679                 e.dom.disabled = true;
24680             });
24681         }
24682         
24683         this.disabled = true;
24684         this.fireEvent("disable", this);
24685         return this;
24686     },
24687
24688     enable : function()
24689     {
24690         if(this.inputType != 'radio'){
24691             Roo.bootstrap.CheckBox.superclass.enable.call(this);
24692             return;
24693         }
24694         
24695         var _this = this;
24696         
24697         if(this.rendered){
24698             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24699                 _this.getActionEl().removeClass(this.disabledClass);
24700                 e.dom.disabled = false;
24701             });
24702         }
24703         
24704         this.disabled = false;
24705         this.fireEvent("enable", this);
24706         return this;
24707     },
24708     
24709     setBoxLabel : function(v)
24710     {
24711         this.boxLabel = v;
24712         
24713         if(this.rendered){
24714             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24715         }
24716     }
24717
24718 });
24719
24720 Roo.apply(Roo.bootstrap.CheckBox, {
24721     
24722     groups: {},
24723     
24724      /**
24725     * register a CheckBox Group
24726     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24727     */
24728     register : function(checkbox)
24729     {
24730         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24731             this.groups[checkbox.groupId] = {};
24732         }
24733         
24734         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24735             return;
24736         }
24737         
24738         this.groups[checkbox.groupId][checkbox.name] = checkbox;
24739         
24740     },
24741     /**
24742     * fetch a CheckBox Group based on the group ID
24743     * @param {string} the group ID
24744     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24745     */
24746     get: function(groupId) {
24747         if (typeof(this.groups[groupId]) == 'undefined') {
24748             return false;
24749         }
24750         
24751         return this.groups[groupId] ;
24752     }
24753     
24754     
24755 });
24756 /*
24757  * - LGPL
24758  *
24759  * RadioItem
24760  * 
24761  */
24762
24763 /**
24764  * @class Roo.bootstrap.Radio
24765  * @extends Roo.bootstrap.Component
24766  * Bootstrap Radio class
24767  * @cfg {String} boxLabel - the label associated
24768  * @cfg {String} value - the value of radio
24769  * 
24770  * @constructor
24771  * Create a new Radio
24772  * @param {Object} config The config object
24773  */
24774 Roo.bootstrap.Radio = function(config){
24775     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24776     
24777 };
24778
24779 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24780     
24781     boxLabel : '',
24782     
24783     value : '',
24784     
24785     getAutoCreate : function()
24786     {
24787         var cfg = {
24788             tag : 'div',
24789             cls : 'form-group radio',
24790             cn : [
24791                 {
24792                     tag : 'label',
24793                     cls : 'box-label',
24794                     html : this.boxLabel
24795                 }
24796             ]
24797         };
24798         
24799         return cfg;
24800     },
24801     
24802     initEvents : function() 
24803     {
24804         this.parent().register(this);
24805         
24806         this.el.on('click', this.onClick, this);
24807         
24808     },
24809     
24810     onClick : function(e)
24811     {
24812         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24813             this.setChecked(true);
24814         }
24815     },
24816     
24817     setChecked : function(state, suppressEvent)
24818     {
24819         this.parent().setValue(this.value, suppressEvent);
24820         
24821     },
24822     
24823     setBoxLabel : function(v)
24824     {
24825         this.boxLabel = v;
24826         
24827         if(this.rendered){
24828             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24829         }
24830     }
24831     
24832 });
24833  
24834
24835  /*
24836  * - LGPL
24837  *
24838  * Input
24839  * 
24840  */
24841
24842 /**
24843  * @class Roo.bootstrap.SecurePass
24844  * @extends Roo.bootstrap.Input
24845  * Bootstrap SecurePass class
24846  *
24847  * 
24848  * @constructor
24849  * Create a new SecurePass
24850  * @param {Object} config The config object
24851  */
24852  
24853 Roo.bootstrap.SecurePass = function (config) {
24854     // these go here, so the translation tool can replace them..
24855     this.errors = {
24856         PwdEmpty: "Please type a password, and then retype it to confirm.",
24857         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24858         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24859         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24860         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24861         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24862         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24863         TooWeak: "Your password is Too Weak."
24864     },
24865     this.meterLabel = "Password strength:";
24866     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24867     this.meterClass = [
24868         "roo-password-meter-tooweak", 
24869         "roo-password-meter-weak", 
24870         "roo-password-meter-medium", 
24871         "roo-password-meter-strong", 
24872         "roo-password-meter-grey"
24873     ];
24874     
24875     this.errors = {};
24876     
24877     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24878 }
24879
24880 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24881     /**
24882      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24883      * {
24884      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24885      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24886      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24887      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24888      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24889      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24890      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24891      * })
24892      */
24893     // private
24894     
24895     meterWidth: 300,
24896     errorMsg :'',    
24897     errors: false,
24898     imageRoot: '/',
24899     /**
24900      * @cfg {String/Object} Label for the strength meter (defaults to
24901      * 'Password strength:')
24902      */
24903     // private
24904     meterLabel: '',
24905     /**
24906      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24907      * ['Weak', 'Medium', 'Strong'])
24908      */
24909     // private    
24910     pwdStrengths: false,    
24911     // private
24912     strength: 0,
24913     // private
24914     _lastPwd: null,
24915     // private
24916     kCapitalLetter: 0,
24917     kSmallLetter: 1,
24918     kDigit: 2,
24919     kPunctuation: 3,
24920     
24921     insecure: false,
24922     // private
24923     initEvents: function ()
24924     {
24925         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24926
24927         if (this.el.is('input[type=password]') && Roo.isSafari) {
24928             this.el.on('keydown', this.SafariOnKeyDown, this);
24929         }
24930
24931         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24932     },
24933     // private
24934     onRender: function (ct, position)
24935     {
24936         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24937         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24938         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24939
24940         this.trigger.createChild({
24941                    cn: [
24942                     {
24943                     //id: 'PwdMeter',
24944                     tag: 'div',
24945                     cls: 'roo-password-meter-grey col-xs-12',
24946                     style: {
24947                         //width: 0,
24948                         //width: this.meterWidth + 'px'                                                
24949                         }
24950                     },
24951                     {                            
24952                          cls: 'roo-password-meter-text'                          
24953                     }
24954                 ]            
24955         });
24956
24957          
24958         if (this.hideTrigger) {
24959             this.trigger.setDisplayed(false);
24960         }
24961         this.setSize(this.width || '', this.height || '');
24962     },
24963     // private
24964     onDestroy: function ()
24965     {
24966         if (this.trigger) {
24967             this.trigger.removeAllListeners();
24968             this.trigger.remove();
24969         }
24970         if (this.wrap) {
24971             this.wrap.remove();
24972         }
24973         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24974     },
24975     // private
24976     checkStrength: function ()
24977     {
24978         var pwd = this.inputEl().getValue();
24979         if (pwd == this._lastPwd) {
24980             return;
24981         }
24982
24983         var strength;
24984         if (this.ClientSideStrongPassword(pwd)) {
24985             strength = 3;
24986         } else if (this.ClientSideMediumPassword(pwd)) {
24987             strength = 2;
24988         } else if (this.ClientSideWeakPassword(pwd)) {
24989             strength = 1;
24990         } else {
24991             strength = 0;
24992         }
24993         
24994         Roo.log('strength1: ' + strength);
24995         
24996         //var pm = this.trigger.child('div/div/div').dom;
24997         var pm = this.trigger.child('div/div');
24998         pm.removeClass(this.meterClass);
24999         pm.addClass(this.meterClass[strength]);
25000                 
25001         
25002         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25003                 
25004         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25005         
25006         this._lastPwd = pwd;
25007     },
25008     reset: function ()
25009     {
25010         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25011         
25012         this._lastPwd = '';
25013         
25014         var pm = this.trigger.child('div/div');
25015         pm.removeClass(this.meterClass);
25016         pm.addClass('roo-password-meter-grey');        
25017         
25018         
25019         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25020         
25021         pt.innerHTML = '';
25022         this.inputEl().dom.type='password';
25023     },
25024     // private
25025     validateValue: function (value)
25026     {
25027         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25028             return false;
25029         }
25030         if (value.length == 0) {
25031             if (this.allowBlank) {
25032                 this.clearInvalid();
25033                 return true;
25034             }
25035
25036             this.markInvalid(this.errors.PwdEmpty);
25037             this.errorMsg = this.errors.PwdEmpty;
25038             return false;
25039         }
25040         
25041         if(this.insecure){
25042             return true;
25043         }
25044         
25045         if (!value.match(/[\x21-\x7e]+/)) {
25046             this.markInvalid(this.errors.PwdBadChar);
25047             this.errorMsg = this.errors.PwdBadChar;
25048             return false;
25049         }
25050         if (value.length < 6) {
25051             this.markInvalid(this.errors.PwdShort);
25052             this.errorMsg = this.errors.PwdShort;
25053             return false;
25054         }
25055         if (value.length > 16) {
25056             this.markInvalid(this.errors.PwdLong);
25057             this.errorMsg = this.errors.PwdLong;
25058             return false;
25059         }
25060         var strength;
25061         if (this.ClientSideStrongPassword(value)) {
25062             strength = 3;
25063         } else if (this.ClientSideMediumPassword(value)) {
25064             strength = 2;
25065         } else if (this.ClientSideWeakPassword(value)) {
25066             strength = 1;
25067         } else {
25068             strength = 0;
25069         }
25070
25071         
25072         if (strength < 2) {
25073             //this.markInvalid(this.errors.TooWeak);
25074             this.errorMsg = this.errors.TooWeak;
25075             //return false;
25076         }
25077         
25078         
25079         console.log('strength2: ' + strength);
25080         
25081         //var pm = this.trigger.child('div/div/div').dom;
25082         
25083         var pm = this.trigger.child('div/div');
25084         pm.removeClass(this.meterClass);
25085         pm.addClass(this.meterClass[strength]);
25086                 
25087         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25088                 
25089         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25090         
25091         this.errorMsg = ''; 
25092         return true;
25093     },
25094     // private
25095     CharacterSetChecks: function (type)
25096     {
25097         this.type = type;
25098         this.fResult = false;
25099     },
25100     // private
25101     isctype: function (character, type)
25102     {
25103         switch (type) {  
25104             case this.kCapitalLetter:
25105                 if (character >= 'A' && character <= 'Z') {
25106                     return true;
25107                 }
25108                 break;
25109             
25110             case this.kSmallLetter:
25111                 if (character >= 'a' && character <= 'z') {
25112                     return true;
25113                 }
25114                 break;
25115             
25116             case this.kDigit:
25117                 if (character >= '0' && character <= '9') {
25118                     return true;
25119                 }
25120                 break;
25121             
25122             case this.kPunctuation:
25123                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25124                     return true;
25125                 }
25126                 break;
25127             
25128             default:
25129                 return false;
25130         }
25131
25132     },
25133     // private
25134     IsLongEnough: function (pwd, size)
25135     {
25136         return !(pwd == null || isNaN(size) || pwd.length < size);
25137     },
25138     // private
25139     SpansEnoughCharacterSets: function (word, nb)
25140     {
25141         if (!this.IsLongEnough(word, nb))
25142         {
25143             return false;
25144         }
25145
25146         var characterSetChecks = new Array(
25147             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25148             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25149         );
25150         
25151         for (var index = 0; index < word.length; ++index) {
25152             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25153                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25154                     characterSetChecks[nCharSet].fResult = true;
25155                     break;
25156                 }
25157             }
25158         }
25159
25160         var nCharSets = 0;
25161         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25162             if (characterSetChecks[nCharSet].fResult) {
25163                 ++nCharSets;
25164             }
25165         }
25166
25167         if (nCharSets < nb) {
25168             return false;
25169         }
25170         return true;
25171     },
25172     // private
25173     ClientSideStrongPassword: function (pwd)
25174     {
25175         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25176     },
25177     // private
25178     ClientSideMediumPassword: function (pwd)
25179     {
25180         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25181     },
25182     // private
25183     ClientSideWeakPassword: function (pwd)
25184     {
25185         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25186     }
25187           
25188 })//<script type="text/javascript">
25189
25190 /*
25191  * Based  Ext JS Library 1.1.1
25192  * Copyright(c) 2006-2007, Ext JS, LLC.
25193  * LGPL
25194  *
25195  */
25196  
25197 /**
25198  * @class Roo.HtmlEditorCore
25199  * @extends Roo.Component
25200  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25201  *
25202  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25203  */
25204
25205 Roo.HtmlEditorCore = function(config){
25206     
25207     
25208     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25209     
25210     
25211     this.addEvents({
25212         /**
25213          * @event initialize
25214          * Fires when the editor is fully initialized (including the iframe)
25215          * @param {Roo.HtmlEditorCore} this
25216          */
25217         initialize: true,
25218         /**
25219          * @event activate
25220          * Fires when the editor is first receives the focus. Any insertion must wait
25221          * until after this event.
25222          * @param {Roo.HtmlEditorCore} this
25223          */
25224         activate: true,
25225          /**
25226          * @event beforesync
25227          * Fires before the textarea is updated with content from the editor iframe. Return false
25228          * to cancel the sync.
25229          * @param {Roo.HtmlEditorCore} this
25230          * @param {String} html
25231          */
25232         beforesync: true,
25233          /**
25234          * @event beforepush
25235          * Fires before the iframe editor is updated with content from the textarea. Return false
25236          * to cancel the push.
25237          * @param {Roo.HtmlEditorCore} this
25238          * @param {String} html
25239          */
25240         beforepush: true,
25241          /**
25242          * @event sync
25243          * Fires when the textarea is updated with content from the editor iframe.
25244          * @param {Roo.HtmlEditorCore} this
25245          * @param {String} html
25246          */
25247         sync: true,
25248          /**
25249          * @event push
25250          * Fires when the iframe editor is updated with content from the textarea.
25251          * @param {Roo.HtmlEditorCore} this
25252          * @param {String} html
25253          */
25254         push: true,
25255         
25256         /**
25257          * @event editorevent
25258          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25259          * @param {Roo.HtmlEditorCore} this
25260          */
25261         editorevent: true
25262         
25263     });
25264     
25265     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25266     
25267     // defaults : white / black...
25268     this.applyBlacklists();
25269     
25270     
25271     
25272 };
25273
25274
25275 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25276
25277
25278      /**
25279      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25280      */
25281     
25282     owner : false,
25283     
25284      /**
25285      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25286      *                        Roo.resizable.
25287      */
25288     resizable : false,
25289      /**
25290      * @cfg {Number} height (in pixels)
25291      */   
25292     height: 300,
25293    /**
25294      * @cfg {Number} width (in pixels)
25295      */   
25296     width: 500,
25297     
25298     /**
25299      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25300      * 
25301      */
25302     stylesheets: false,
25303     
25304     // id of frame..
25305     frameId: false,
25306     
25307     // private properties
25308     validationEvent : false,
25309     deferHeight: true,
25310     initialized : false,
25311     activated : false,
25312     sourceEditMode : false,
25313     onFocus : Roo.emptyFn,
25314     iframePad:3,
25315     hideMode:'offsets',
25316     
25317     clearUp: true,
25318     
25319     // blacklist + whitelisted elements..
25320     black: false,
25321     white: false,
25322      
25323     bodyCls : '',
25324
25325     /**
25326      * Protected method that will not generally be called directly. It
25327      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25328      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25329      */
25330     getDocMarkup : function(){
25331         // body styles..
25332         var st = '';
25333         
25334         // inherit styels from page...?? 
25335         if (this.stylesheets === false) {
25336             
25337             Roo.get(document.head).select('style').each(function(node) {
25338                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25339             });
25340             
25341             Roo.get(document.head).select('link').each(function(node) { 
25342                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25343             });
25344             
25345         } else if (!this.stylesheets.length) {
25346                 // simple..
25347                 st = '<style type="text/css">' +
25348                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25349                    '</style>';
25350         } else {
25351             for (var i in this.stylesheets) { 
25352                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25353             }
25354             
25355         }
25356         
25357         st +=  '<style type="text/css">' +
25358             'IMG { cursor: pointer } ' +
25359         '</style>';
25360
25361         var cls = 'roo-htmleditor-body';
25362         
25363         if(this.bodyCls.length){
25364             cls += ' ' + this.bodyCls;
25365         }
25366         
25367         return '<html><head>' + st  +
25368             //<style type="text/css">' +
25369             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25370             //'</style>' +
25371             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25372     },
25373
25374     // private
25375     onRender : function(ct, position)
25376     {
25377         var _t = this;
25378         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25379         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25380         
25381         
25382         this.el.dom.style.border = '0 none';
25383         this.el.dom.setAttribute('tabIndex', -1);
25384         this.el.addClass('x-hidden hide');
25385         
25386         
25387         
25388         if(Roo.isIE){ // fix IE 1px bogus margin
25389             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25390         }
25391        
25392         
25393         this.frameId = Roo.id();
25394         
25395          
25396         
25397         var iframe = this.owner.wrap.createChild({
25398             tag: 'iframe',
25399             cls: 'form-control', // bootstrap..
25400             id: this.frameId,
25401             name: this.frameId,
25402             frameBorder : 'no',
25403             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25404         }, this.el
25405         );
25406         
25407         
25408         this.iframe = iframe.dom;
25409
25410          this.assignDocWin();
25411         
25412         this.doc.designMode = 'on';
25413        
25414         this.doc.open();
25415         this.doc.write(this.getDocMarkup());
25416         this.doc.close();
25417
25418         
25419         var task = { // must defer to wait for browser to be ready
25420             run : function(){
25421                 //console.log("run task?" + this.doc.readyState);
25422                 this.assignDocWin();
25423                 if(this.doc.body || this.doc.readyState == 'complete'){
25424                     try {
25425                         this.doc.designMode="on";
25426                     } catch (e) {
25427                         return;
25428                     }
25429                     Roo.TaskMgr.stop(task);
25430                     this.initEditor.defer(10, this);
25431                 }
25432             },
25433             interval : 10,
25434             duration: 10000,
25435             scope: this
25436         };
25437         Roo.TaskMgr.start(task);
25438
25439     },
25440
25441     // private
25442     onResize : function(w, h)
25443     {
25444          Roo.log('resize: ' +w + ',' + h );
25445         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25446         if(!this.iframe){
25447             return;
25448         }
25449         if(typeof w == 'number'){
25450             
25451             this.iframe.style.width = w + 'px';
25452         }
25453         if(typeof h == 'number'){
25454             
25455             this.iframe.style.height = h + 'px';
25456             if(this.doc){
25457                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25458             }
25459         }
25460         
25461     },
25462
25463     /**
25464      * Toggles the editor between standard and source edit mode.
25465      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25466      */
25467     toggleSourceEdit : function(sourceEditMode){
25468         
25469         this.sourceEditMode = sourceEditMode === true;
25470         
25471         if(this.sourceEditMode){
25472  
25473             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25474             
25475         }else{
25476             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25477             //this.iframe.className = '';
25478             this.deferFocus();
25479         }
25480         //this.setSize(this.owner.wrap.getSize());
25481         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25482     },
25483
25484     
25485   
25486
25487     /**
25488      * Protected method that will not generally be called directly. If you need/want
25489      * custom HTML cleanup, this is the method you should override.
25490      * @param {String} html The HTML to be cleaned
25491      * return {String} The cleaned HTML
25492      */
25493     cleanHtml : function(html){
25494         html = String(html);
25495         if(html.length > 5){
25496             if(Roo.isSafari){ // strip safari nonsense
25497                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25498             }
25499         }
25500         if(html == '&nbsp;'){
25501             html = '';
25502         }
25503         return html;
25504     },
25505
25506     /**
25507      * HTML Editor -> Textarea
25508      * Protected method that will not generally be called directly. Syncs the contents
25509      * of the editor iframe with the textarea.
25510      */
25511     syncValue : function(){
25512         if(this.initialized){
25513             var bd = (this.doc.body || this.doc.documentElement);
25514             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25515             var html = bd.innerHTML;
25516             if(Roo.isSafari){
25517                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25518                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25519                 if(m && m[1]){
25520                     html = '<div style="'+m[0]+'">' + html + '</div>';
25521                 }
25522             }
25523             html = this.cleanHtml(html);
25524             // fix up the special chars.. normaly like back quotes in word...
25525             // however we do not want to do this with chinese..
25526             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25527                 
25528                 var cc = match.charCodeAt();
25529
25530                 // Get the character value, handling surrogate pairs
25531                 if (match.length == 2) {
25532                     // It's a surrogate pair, calculate the Unicode code point
25533                     var high = match.charCodeAt(0) - 0xD800;
25534                     var low  = match.charCodeAt(1) - 0xDC00;
25535                     cc = (high * 0x400) + low + 0x10000;
25536                 }  else if (
25537                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25538                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25539                     (cc >= 0xf900 && cc < 0xfb00 )
25540                 ) {
25541                         return match;
25542                 }  
25543          
25544                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25545                 return "&#" + cc + ";";
25546                 
25547                 
25548             });
25549             
25550             
25551              
25552             if(this.owner.fireEvent('beforesync', this, html) !== false){
25553                 this.el.dom.value = html;
25554                 this.owner.fireEvent('sync', this, html);
25555             }
25556         }
25557     },
25558
25559     /**
25560      * Protected method that will not generally be called directly. Pushes the value of the textarea
25561      * into the iframe editor.
25562      */
25563     pushValue : function(){
25564         if(this.initialized){
25565             var v = this.el.dom.value.trim();
25566             
25567 //            if(v.length < 1){
25568 //                v = '&#160;';
25569 //            }
25570             
25571             if(this.owner.fireEvent('beforepush', this, v) !== false){
25572                 var d = (this.doc.body || this.doc.documentElement);
25573                 d.innerHTML = v;
25574                 this.cleanUpPaste();
25575                 this.el.dom.value = d.innerHTML;
25576                 this.owner.fireEvent('push', this, v);
25577             }
25578         }
25579     },
25580
25581     // private
25582     deferFocus : function(){
25583         this.focus.defer(10, this);
25584     },
25585
25586     // doc'ed in Field
25587     focus : function(){
25588         if(this.win && !this.sourceEditMode){
25589             this.win.focus();
25590         }else{
25591             this.el.focus();
25592         }
25593     },
25594     
25595     assignDocWin: function()
25596     {
25597         var iframe = this.iframe;
25598         
25599          if(Roo.isIE){
25600             this.doc = iframe.contentWindow.document;
25601             this.win = iframe.contentWindow;
25602         } else {
25603 //            if (!Roo.get(this.frameId)) {
25604 //                return;
25605 //            }
25606 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25607 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25608             
25609             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25610                 return;
25611             }
25612             
25613             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25614             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25615         }
25616     },
25617     
25618     // private
25619     initEditor : function(){
25620         //console.log("INIT EDITOR");
25621         this.assignDocWin();
25622         
25623         
25624         
25625         this.doc.designMode="on";
25626         this.doc.open();
25627         this.doc.write(this.getDocMarkup());
25628         this.doc.close();
25629         
25630         var dbody = (this.doc.body || this.doc.documentElement);
25631         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25632         // this copies styles from the containing element into thsi one..
25633         // not sure why we need all of this..
25634         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25635         
25636         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25637         //ss['background-attachment'] = 'fixed'; // w3c
25638         dbody.bgProperties = 'fixed'; // ie
25639         //Roo.DomHelper.applyStyles(dbody, ss);
25640         Roo.EventManager.on(this.doc, {
25641             //'mousedown': this.onEditorEvent,
25642             'mouseup': this.onEditorEvent,
25643             'dblclick': this.onEditorEvent,
25644             'click': this.onEditorEvent,
25645             'keyup': this.onEditorEvent,
25646             buffer:100,
25647             scope: this
25648         });
25649         if(Roo.isGecko){
25650             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25651         }
25652         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25653             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25654         }
25655         this.initialized = true;
25656
25657         this.owner.fireEvent('initialize', this);
25658         this.pushValue();
25659     },
25660
25661     // private
25662     onDestroy : function(){
25663         
25664         
25665         
25666         if(this.rendered){
25667             
25668             //for (var i =0; i < this.toolbars.length;i++) {
25669             //    // fixme - ask toolbars for heights?
25670             //    this.toolbars[i].onDestroy();
25671            // }
25672             
25673             //this.wrap.dom.innerHTML = '';
25674             //this.wrap.remove();
25675         }
25676     },
25677
25678     // private
25679     onFirstFocus : function(){
25680         
25681         this.assignDocWin();
25682         
25683         
25684         this.activated = true;
25685          
25686     
25687         if(Roo.isGecko){ // prevent silly gecko errors
25688             this.win.focus();
25689             var s = this.win.getSelection();
25690             if(!s.focusNode || s.focusNode.nodeType != 3){
25691                 var r = s.getRangeAt(0);
25692                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25693                 r.collapse(true);
25694                 this.deferFocus();
25695             }
25696             try{
25697                 this.execCmd('useCSS', true);
25698                 this.execCmd('styleWithCSS', false);
25699             }catch(e){}
25700         }
25701         this.owner.fireEvent('activate', this);
25702     },
25703
25704     // private
25705     adjustFont: function(btn){
25706         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25707         //if(Roo.isSafari){ // safari
25708         //    adjust *= 2;
25709        // }
25710         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25711         if(Roo.isSafari){ // safari
25712             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25713             v =  (v < 10) ? 10 : v;
25714             v =  (v > 48) ? 48 : v;
25715             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25716             
25717         }
25718         
25719         
25720         v = Math.max(1, v+adjust);
25721         
25722         this.execCmd('FontSize', v  );
25723     },
25724
25725     onEditorEvent : function(e)
25726     {
25727         this.owner.fireEvent('editorevent', this, e);
25728       //  this.updateToolbar();
25729         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25730     },
25731
25732     insertTag : function(tg)
25733     {
25734         // could be a bit smarter... -> wrap the current selected tRoo..
25735         if (tg.toLowerCase() == 'span' ||
25736             tg.toLowerCase() == 'code' ||
25737             tg.toLowerCase() == 'sup' ||
25738             tg.toLowerCase() == 'sub' 
25739             ) {
25740             
25741             range = this.createRange(this.getSelection());
25742             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25743             wrappingNode.appendChild(range.extractContents());
25744             range.insertNode(wrappingNode);
25745
25746             return;
25747             
25748             
25749             
25750         }
25751         this.execCmd("formatblock",   tg);
25752         
25753     },
25754     
25755     insertText : function(txt)
25756     {
25757         
25758         
25759         var range = this.createRange();
25760         range.deleteContents();
25761                //alert(Sender.getAttribute('label'));
25762                
25763         range.insertNode(this.doc.createTextNode(txt));
25764     } ,
25765     
25766      
25767
25768     /**
25769      * Executes a Midas editor command on the editor document and performs necessary focus and
25770      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25771      * @param {String} cmd The Midas command
25772      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25773      */
25774     relayCmd : function(cmd, value){
25775         this.win.focus();
25776         this.execCmd(cmd, value);
25777         this.owner.fireEvent('editorevent', this);
25778         //this.updateToolbar();
25779         this.owner.deferFocus();
25780     },
25781
25782     /**
25783      * Executes a Midas editor command directly on the editor document.
25784      * For visual commands, you should use {@link #relayCmd} instead.
25785      * <b>This should only be called after the editor is initialized.</b>
25786      * @param {String} cmd The Midas command
25787      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25788      */
25789     execCmd : function(cmd, value){
25790         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25791         this.syncValue();
25792     },
25793  
25794  
25795    
25796     /**
25797      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25798      * to insert tRoo.
25799      * @param {String} text | dom node.. 
25800      */
25801     insertAtCursor : function(text)
25802     {
25803         
25804         if(!this.activated){
25805             return;
25806         }
25807         /*
25808         if(Roo.isIE){
25809             this.win.focus();
25810             var r = this.doc.selection.createRange();
25811             if(r){
25812                 r.collapse(true);
25813                 r.pasteHTML(text);
25814                 this.syncValue();
25815                 this.deferFocus();
25816             
25817             }
25818             return;
25819         }
25820         */
25821         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25822             this.win.focus();
25823             
25824             
25825             // from jquery ui (MIT licenced)
25826             var range, node;
25827             var win = this.win;
25828             
25829             if (win.getSelection && win.getSelection().getRangeAt) {
25830                 range = win.getSelection().getRangeAt(0);
25831                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25832                 range.insertNode(node);
25833             } else if (win.document.selection && win.document.selection.createRange) {
25834                 // no firefox support
25835                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25836                 win.document.selection.createRange().pasteHTML(txt);
25837             } else {
25838                 // no firefox support
25839                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25840                 this.execCmd('InsertHTML', txt);
25841             } 
25842             
25843             this.syncValue();
25844             
25845             this.deferFocus();
25846         }
25847     },
25848  // private
25849     mozKeyPress : function(e){
25850         if(e.ctrlKey){
25851             var c = e.getCharCode(), cmd;
25852           
25853             if(c > 0){
25854                 c = String.fromCharCode(c).toLowerCase();
25855                 switch(c){
25856                     case 'b':
25857                         cmd = 'bold';
25858                         break;
25859                     case 'i':
25860                         cmd = 'italic';
25861                         break;
25862                     
25863                     case 'u':
25864                         cmd = 'underline';
25865                         break;
25866                     
25867                     case 'v':
25868                         this.cleanUpPaste.defer(100, this);
25869                         return;
25870                         
25871                 }
25872                 if(cmd){
25873                     this.win.focus();
25874                     this.execCmd(cmd);
25875                     this.deferFocus();
25876                     e.preventDefault();
25877                 }
25878                 
25879             }
25880         }
25881     },
25882
25883     // private
25884     fixKeys : function(){ // load time branching for fastest keydown performance
25885         if(Roo.isIE){
25886             return function(e){
25887                 var k = e.getKey(), r;
25888                 if(k == e.TAB){
25889                     e.stopEvent();
25890                     r = this.doc.selection.createRange();
25891                     if(r){
25892                         r.collapse(true);
25893                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25894                         this.deferFocus();
25895                     }
25896                     return;
25897                 }
25898                 
25899                 if(k == e.ENTER){
25900                     r = this.doc.selection.createRange();
25901                     if(r){
25902                         var target = r.parentElement();
25903                         if(!target || target.tagName.toLowerCase() != 'li'){
25904                             e.stopEvent();
25905                             r.pasteHTML('<br />');
25906                             r.collapse(false);
25907                             r.select();
25908                         }
25909                     }
25910                 }
25911                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25912                     this.cleanUpPaste.defer(100, this);
25913                     return;
25914                 }
25915                 
25916                 
25917             };
25918         }else if(Roo.isOpera){
25919             return function(e){
25920                 var k = e.getKey();
25921                 if(k == e.TAB){
25922                     e.stopEvent();
25923                     this.win.focus();
25924                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25925                     this.deferFocus();
25926                 }
25927                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25928                     this.cleanUpPaste.defer(100, this);
25929                     return;
25930                 }
25931                 
25932             };
25933         }else if(Roo.isSafari){
25934             return function(e){
25935                 var k = e.getKey();
25936                 
25937                 if(k == e.TAB){
25938                     e.stopEvent();
25939                     this.execCmd('InsertText','\t');
25940                     this.deferFocus();
25941                     return;
25942                 }
25943                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25944                     this.cleanUpPaste.defer(100, this);
25945                     return;
25946                 }
25947                 
25948              };
25949         }
25950     }(),
25951     
25952     getAllAncestors: function()
25953     {
25954         var p = this.getSelectedNode();
25955         var a = [];
25956         if (!p) {
25957             a.push(p); // push blank onto stack..
25958             p = this.getParentElement();
25959         }
25960         
25961         
25962         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25963             a.push(p);
25964             p = p.parentNode;
25965         }
25966         a.push(this.doc.body);
25967         return a;
25968     },
25969     lastSel : false,
25970     lastSelNode : false,
25971     
25972     
25973     getSelection : function() 
25974     {
25975         this.assignDocWin();
25976         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25977     },
25978     
25979     getSelectedNode: function() 
25980     {
25981         // this may only work on Gecko!!!
25982         
25983         // should we cache this!!!!
25984         
25985         
25986         
25987          
25988         var range = this.createRange(this.getSelection()).cloneRange();
25989         
25990         if (Roo.isIE) {
25991             var parent = range.parentElement();
25992             while (true) {
25993                 var testRange = range.duplicate();
25994                 testRange.moveToElementText(parent);
25995                 if (testRange.inRange(range)) {
25996                     break;
25997                 }
25998                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25999                     break;
26000                 }
26001                 parent = parent.parentElement;
26002             }
26003             return parent;
26004         }
26005         
26006         // is ancestor a text element.
26007         var ac =  range.commonAncestorContainer;
26008         if (ac.nodeType == 3) {
26009             ac = ac.parentNode;
26010         }
26011         
26012         var ar = ac.childNodes;
26013          
26014         var nodes = [];
26015         var other_nodes = [];
26016         var has_other_nodes = false;
26017         for (var i=0;i<ar.length;i++) {
26018             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26019                 continue;
26020             }
26021             // fullly contained node.
26022             
26023             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26024                 nodes.push(ar[i]);
26025                 continue;
26026             }
26027             
26028             // probably selected..
26029             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26030                 other_nodes.push(ar[i]);
26031                 continue;
26032             }
26033             // outer..
26034             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26035                 continue;
26036             }
26037             
26038             
26039             has_other_nodes = true;
26040         }
26041         if (!nodes.length && other_nodes.length) {
26042             nodes= other_nodes;
26043         }
26044         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26045             return false;
26046         }
26047         
26048         return nodes[0];
26049     },
26050     createRange: function(sel)
26051     {
26052         // this has strange effects when using with 
26053         // top toolbar - not sure if it's a great idea.
26054         //this.editor.contentWindow.focus();
26055         if (typeof sel != "undefined") {
26056             try {
26057                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26058             } catch(e) {
26059                 return this.doc.createRange();
26060             }
26061         } else {
26062             return this.doc.createRange();
26063         }
26064     },
26065     getParentElement: function()
26066     {
26067         
26068         this.assignDocWin();
26069         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26070         
26071         var range = this.createRange(sel);
26072          
26073         try {
26074             var p = range.commonAncestorContainer;
26075             while (p.nodeType == 3) { // text node
26076                 p = p.parentNode;
26077             }
26078             return p;
26079         } catch (e) {
26080             return null;
26081         }
26082     
26083     },
26084     /***
26085      *
26086      * Range intersection.. the hard stuff...
26087      *  '-1' = before
26088      *  '0' = hits..
26089      *  '1' = after.
26090      *         [ -- selected range --- ]
26091      *   [fail]                        [fail]
26092      *
26093      *    basically..
26094      *      if end is before start or  hits it. fail.
26095      *      if start is after end or hits it fail.
26096      *
26097      *   if either hits (but other is outside. - then it's not 
26098      *   
26099      *    
26100      **/
26101     
26102     
26103     // @see http://www.thismuchiknow.co.uk/?p=64.
26104     rangeIntersectsNode : function(range, node)
26105     {
26106         var nodeRange = node.ownerDocument.createRange();
26107         try {
26108             nodeRange.selectNode(node);
26109         } catch (e) {
26110             nodeRange.selectNodeContents(node);
26111         }
26112     
26113         var rangeStartRange = range.cloneRange();
26114         rangeStartRange.collapse(true);
26115     
26116         var rangeEndRange = range.cloneRange();
26117         rangeEndRange.collapse(false);
26118     
26119         var nodeStartRange = nodeRange.cloneRange();
26120         nodeStartRange.collapse(true);
26121     
26122         var nodeEndRange = nodeRange.cloneRange();
26123         nodeEndRange.collapse(false);
26124     
26125         return rangeStartRange.compareBoundaryPoints(
26126                  Range.START_TO_START, nodeEndRange) == -1 &&
26127                rangeEndRange.compareBoundaryPoints(
26128                  Range.START_TO_START, nodeStartRange) == 1;
26129         
26130          
26131     },
26132     rangeCompareNode : function(range, node)
26133     {
26134         var nodeRange = node.ownerDocument.createRange();
26135         try {
26136             nodeRange.selectNode(node);
26137         } catch (e) {
26138             nodeRange.selectNodeContents(node);
26139         }
26140         
26141         
26142         range.collapse(true);
26143     
26144         nodeRange.collapse(true);
26145      
26146         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26147         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26148          
26149         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26150         
26151         var nodeIsBefore   =  ss == 1;
26152         var nodeIsAfter    = ee == -1;
26153         
26154         if (nodeIsBefore && nodeIsAfter) {
26155             return 0; // outer
26156         }
26157         if (!nodeIsBefore && nodeIsAfter) {
26158             return 1; //right trailed.
26159         }
26160         
26161         if (nodeIsBefore && !nodeIsAfter) {
26162             return 2;  // left trailed.
26163         }
26164         // fully contined.
26165         return 3;
26166     },
26167
26168     // private? - in a new class?
26169     cleanUpPaste :  function()
26170     {
26171         // cleans up the whole document..
26172         Roo.log('cleanuppaste');
26173         
26174         this.cleanUpChildren(this.doc.body);
26175         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26176         if (clean != this.doc.body.innerHTML) {
26177             this.doc.body.innerHTML = clean;
26178         }
26179         
26180     },
26181     
26182     cleanWordChars : function(input) {// change the chars to hex code
26183         var he = Roo.HtmlEditorCore;
26184         
26185         var output = input;
26186         Roo.each(he.swapCodes, function(sw) { 
26187             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26188             
26189             output = output.replace(swapper, sw[1]);
26190         });
26191         
26192         return output;
26193     },
26194     
26195     
26196     cleanUpChildren : function (n)
26197     {
26198         if (!n.childNodes.length) {
26199             return;
26200         }
26201         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26202            this.cleanUpChild(n.childNodes[i]);
26203         }
26204     },
26205     
26206     
26207         
26208     
26209     cleanUpChild : function (node)
26210     {
26211         var ed = this;
26212         //console.log(node);
26213         if (node.nodeName == "#text") {
26214             // clean up silly Windows -- stuff?
26215             return; 
26216         }
26217         if (node.nodeName == "#comment") {
26218             node.parentNode.removeChild(node);
26219             // clean up silly Windows -- stuff?
26220             return; 
26221         }
26222         var lcname = node.tagName.toLowerCase();
26223         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26224         // whitelist of tags..
26225         
26226         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26227             // remove node.
26228             node.parentNode.removeChild(node);
26229             return;
26230             
26231         }
26232         
26233         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26234         
26235         // spans with no attributes - just remove them..
26236         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26237             remove_keep_children = true;
26238         }
26239         
26240         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26241         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26242         
26243         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26244         //    remove_keep_children = true;
26245         //}
26246         
26247         if (remove_keep_children) {
26248             this.cleanUpChildren(node);
26249             // inserts everything just before this node...
26250             while (node.childNodes.length) {
26251                 var cn = node.childNodes[0];
26252                 node.removeChild(cn);
26253                 node.parentNode.insertBefore(cn, node);
26254             }
26255             node.parentNode.removeChild(node);
26256             return;
26257         }
26258         
26259         if (!node.attributes || !node.attributes.length) {
26260             
26261           
26262             
26263             
26264             this.cleanUpChildren(node);
26265             return;
26266         }
26267         
26268         function cleanAttr(n,v)
26269         {
26270             
26271             if (v.match(/^\./) || v.match(/^\//)) {
26272                 return;
26273             }
26274             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26275                 return;
26276             }
26277             if (v.match(/^#/)) {
26278                 return;
26279             }
26280             if (v.match(/^\{/)) { // allow template editing.
26281                 return;
26282             }
26283 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26284             node.removeAttribute(n);
26285             
26286         }
26287         
26288         var cwhite = this.cwhite;
26289         var cblack = this.cblack;
26290             
26291         function cleanStyle(n,v)
26292         {
26293             if (v.match(/expression/)) { //XSS?? should we even bother..
26294                 node.removeAttribute(n);
26295                 return;
26296             }
26297             
26298             var parts = v.split(/;/);
26299             var clean = [];
26300             
26301             Roo.each(parts, function(p) {
26302                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26303                 if (!p.length) {
26304                     return true;
26305                 }
26306                 var l = p.split(':').shift().replace(/\s+/g,'');
26307                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26308                 
26309                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26310 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26311                     //node.removeAttribute(n);
26312                     return true;
26313                 }
26314                 //Roo.log()
26315                 // only allow 'c whitelisted system attributes'
26316                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26317 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26318                     //node.removeAttribute(n);
26319                     return true;
26320                 }
26321                 
26322                 
26323                  
26324                 
26325                 clean.push(p);
26326                 return true;
26327             });
26328             if (clean.length) { 
26329                 node.setAttribute(n, clean.join(';'));
26330             } else {
26331                 node.removeAttribute(n);
26332             }
26333             
26334         }
26335         
26336         
26337         for (var i = node.attributes.length-1; i > -1 ; i--) {
26338             var a = node.attributes[i];
26339             //console.log(a);
26340             
26341             if (a.name.toLowerCase().substr(0,2)=='on')  {
26342                 node.removeAttribute(a.name);
26343                 continue;
26344             }
26345             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26346                 node.removeAttribute(a.name);
26347                 continue;
26348             }
26349             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26350                 cleanAttr(a.name,a.value); // fixme..
26351                 continue;
26352             }
26353             if (a.name == 'style') {
26354                 cleanStyle(a.name,a.value);
26355                 continue;
26356             }
26357             /// clean up MS crap..
26358             // tecnically this should be a list of valid class'es..
26359             
26360             
26361             if (a.name == 'class') {
26362                 if (a.value.match(/^Mso/)) {
26363                     node.removeAttribute('class');
26364                 }
26365                 
26366                 if (a.value.match(/^body$/)) {
26367                     node.removeAttribute('class');
26368                 }
26369                 continue;
26370             }
26371             
26372             // style cleanup!?
26373             // class cleanup?
26374             
26375         }
26376         
26377         
26378         this.cleanUpChildren(node);
26379         
26380         
26381     },
26382     
26383     /**
26384      * Clean up MS wordisms...
26385      */
26386     cleanWord : function(node)
26387     {
26388         if (!node) {
26389             this.cleanWord(this.doc.body);
26390             return;
26391         }
26392         
26393         if(
26394                 node.nodeName == 'SPAN' &&
26395                 !node.hasAttributes() &&
26396                 node.childNodes.length == 1 &&
26397                 node.firstChild.nodeName == "#text"  
26398         ) {
26399             var textNode = node.firstChild;
26400             node.removeChild(textNode);
26401             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26402                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26403             }
26404             node.parentNode.insertBefore(textNode, node);
26405             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26406                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26407             }
26408             node.parentNode.removeChild(node);
26409         }
26410         
26411         if (node.nodeName == "#text") {
26412             // clean up silly Windows -- stuff?
26413             return; 
26414         }
26415         if (node.nodeName == "#comment") {
26416             node.parentNode.removeChild(node);
26417             // clean up silly Windows -- stuff?
26418             return; 
26419         }
26420         
26421         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26422             node.parentNode.removeChild(node);
26423             return;
26424         }
26425         //Roo.log(node.tagName);
26426         // remove - but keep children..
26427         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26428             //Roo.log('-- removed');
26429             while (node.childNodes.length) {
26430                 var cn = node.childNodes[0];
26431                 node.removeChild(cn);
26432                 node.parentNode.insertBefore(cn, node);
26433                 // move node to parent - and clean it..
26434                 this.cleanWord(cn);
26435             }
26436             node.parentNode.removeChild(node);
26437             /// no need to iterate chidlren = it's got none..
26438             //this.iterateChildren(node, this.cleanWord);
26439             return;
26440         }
26441         // clean styles
26442         if (node.className.length) {
26443             
26444             var cn = node.className.split(/\W+/);
26445             var cna = [];
26446             Roo.each(cn, function(cls) {
26447                 if (cls.match(/Mso[a-zA-Z]+/)) {
26448                     return;
26449                 }
26450                 cna.push(cls);
26451             });
26452             node.className = cna.length ? cna.join(' ') : '';
26453             if (!cna.length) {
26454                 node.removeAttribute("class");
26455             }
26456         }
26457         
26458         if (node.hasAttribute("lang")) {
26459             node.removeAttribute("lang");
26460         }
26461         
26462         if (node.hasAttribute("style")) {
26463             
26464             var styles = node.getAttribute("style").split(";");
26465             var nstyle = [];
26466             Roo.each(styles, function(s) {
26467                 if (!s.match(/:/)) {
26468                     return;
26469                 }
26470                 var kv = s.split(":");
26471                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26472                     return;
26473                 }
26474                 // what ever is left... we allow.
26475                 nstyle.push(s);
26476             });
26477             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26478             if (!nstyle.length) {
26479                 node.removeAttribute('style');
26480             }
26481         }
26482         this.iterateChildren(node, this.cleanWord);
26483         
26484         
26485         
26486     },
26487     /**
26488      * iterateChildren of a Node, calling fn each time, using this as the scole..
26489      * @param {DomNode} node node to iterate children of.
26490      * @param {Function} fn method of this class to call on each item.
26491      */
26492     iterateChildren : function(node, fn)
26493     {
26494         if (!node.childNodes.length) {
26495                 return;
26496         }
26497         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26498            fn.call(this, node.childNodes[i])
26499         }
26500     },
26501     
26502     
26503     /**
26504      * cleanTableWidths.
26505      *
26506      * Quite often pasting from word etc.. results in tables with column and widths.
26507      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26508      *
26509      */
26510     cleanTableWidths : function(node)
26511     {
26512          
26513          
26514         if (!node) {
26515             this.cleanTableWidths(this.doc.body);
26516             return;
26517         }
26518         
26519         // ignore list...
26520         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26521             return; 
26522         }
26523         Roo.log(node.tagName);
26524         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26525             this.iterateChildren(node, this.cleanTableWidths);
26526             return;
26527         }
26528         if (node.hasAttribute('width')) {
26529             node.removeAttribute('width');
26530         }
26531         
26532          
26533         if (node.hasAttribute("style")) {
26534             // pretty basic...
26535             
26536             var styles = node.getAttribute("style").split(";");
26537             var nstyle = [];
26538             Roo.each(styles, function(s) {
26539                 if (!s.match(/:/)) {
26540                     return;
26541                 }
26542                 var kv = s.split(":");
26543                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26544                     return;
26545                 }
26546                 // what ever is left... we allow.
26547                 nstyle.push(s);
26548             });
26549             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26550             if (!nstyle.length) {
26551                 node.removeAttribute('style');
26552             }
26553         }
26554         
26555         this.iterateChildren(node, this.cleanTableWidths);
26556         
26557         
26558     },
26559     
26560     
26561     
26562     
26563     domToHTML : function(currentElement, depth, nopadtext) {
26564         
26565         depth = depth || 0;
26566         nopadtext = nopadtext || false;
26567     
26568         if (!currentElement) {
26569             return this.domToHTML(this.doc.body);
26570         }
26571         
26572         //Roo.log(currentElement);
26573         var j;
26574         var allText = false;
26575         var nodeName = currentElement.nodeName;
26576         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26577         
26578         if  (nodeName == '#text') {
26579             
26580             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26581         }
26582         
26583         
26584         var ret = '';
26585         if (nodeName != 'BODY') {
26586              
26587             var i = 0;
26588             // Prints the node tagName, such as <A>, <IMG>, etc
26589             if (tagName) {
26590                 var attr = [];
26591                 for(i = 0; i < currentElement.attributes.length;i++) {
26592                     // quoting?
26593                     var aname = currentElement.attributes.item(i).name;
26594                     if (!currentElement.attributes.item(i).value.length) {
26595                         continue;
26596                     }
26597                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26598                 }
26599                 
26600                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26601             } 
26602             else {
26603                 
26604                 // eack
26605             }
26606         } else {
26607             tagName = false;
26608         }
26609         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26610             return ret;
26611         }
26612         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26613             nopadtext = true;
26614         }
26615         
26616         
26617         // Traverse the tree
26618         i = 0;
26619         var currentElementChild = currentElement.childNodes.item(i);
26620         var allText = true;
26621         var innerHTML  = '';
26622         lastnode = '';
26623         while (currentElementChild) {
26624             // Formatting code (indent the tree so it looks nice on the screen)
26625             var nopad = nopadtext;
26626             if (lastnode == 'SPAN') {
26627                 nopad  = true;
26628             }
26629             // text
26630             if  (currentElementChild.nodeName == '#text') {
26631                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26632                 toadd = nopadtext ? toadd : toadd.trim();
26633                 if (!nopad && toadd.length > 80) {
26634                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26635                 }
26636                 innerHTML  += toadd;
26637                 
26638                 i++;
26639                 currentElementChild = currentElement.childNodes.item(i);
26640                 lastNode = '';
26641                 continue;
26642             }
26643             allText = false;
26644             
26645             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26646                 
26647             // Recursively traverse the tree structure of the child node
26648             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26649             lastnode = currentElementChild.nodeName;
26650             i++;
26651             currentElementChild=currentElement.childNodes.item(i);
26652         }
26653         
26654         ret += innerHTML;
26655         
26656         if (!allText) {
26657                 // The remaining code is mostly for formatting the tree
26658             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26659         }
26660         
26661         
26662         if (tagName) {
26663             ret+= "</"+tagName+">";
26664         }
26665         return ret;
26666         
26667     },
26668         
26669     applyBlacklists : function()
26670     {
26671         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26672         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26673         
26674         this.white = [];
26675         this.black = [];
26676         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26677             if (b.indexOf(tag) > -1) {
26678                 return;
26679             }
26680             this.white.push(tag);
26681             
26682         }, this);
26683         
26684         Roo.each(w, function(tag) {
26685             if (b.indexOf(tag) > -1) {
26686                 return;
26687             }
26688             if (this.white.indexOf(tag) > -1) {
26689                 return;
26690             }
26691             this.white.push(tag);
26692             
26693         }, this);
26694         
26695         
26696         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26697             if (w.indexOf(tag) > -1) {
26698                 return;
26699             }
26700             this.black.push(tag);
26701             
26702         }, this);
26703         
26704         Roo.each(b, function(tag) {
26705             if (w.indexOf(tag) > -1) {
26706                 return;
26707             }
26708             if (this.black.indexOf(tag) > -1) {
26709                 return;
26710             }
26711             this.black.push(tag);
26712             
26713         }, this);
26714         
26715         
26716         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26717         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26718         
26719         this.cwhite = [];
26720         this.cblack = [];
26721         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26722             if (b.indexOf(tag) > -1) {
26723                 return;
26724             }
26725             this.cwhite.push(tag);
26726             
26727         }, this);
26728         
26729         Roo.each(w, function(tag) {
26730             if (b.indexOf(tag) > -1) {
26731                 return;
26732             }
26733             if (this.cwhite.indexOf(tag) > -1) {
26734                 return;
26735             }
26736             this.cwhite.push(tag);
26737             
26738         }, this);
26739         
26740         
26741         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26742             if (w.indexOf(tag) > -1) {
26743                 return;
26744             }
26745             this.cblack.push(tag);
26746             
26747         }, this);
26748         
26749         Roo.each(b, function(tag) {
26750             if (w.indexOf(tag) > -1) {
26751                 return;
26752             }
26753             if (this.cblack.indexOf(tag) > -1) {
26754                 return;
26755             }
26756             this.cblack.push(tag);
26757             
26758         }, this);
26759     },
26760     
26761     setStylesheets : function(stylesheets)
26762     {
26763         if(typeof(stylesheets) == 'string'){
26764             Roo.get(this.iframe.contentDocument.head).createChild({
26765                 tag : 'link',
26766                 rel : 'stylesheet',
26767                 type : 'text/css',
26768                 href : stylesheets
26769             });
26770             
26771             return;
26772         }
26773         var _this = this;
26774      
26775         Roo.each(stylesheets, function(s) {
26776             if(!s.length){
26777                 return;
26778             }
26779             
26780             Roo.get(_this.iframe.contentDocument.head).createChild({
26781                 tag : 'link',
26782                 rel : 'stylesheet',
26783                 type : 'text/css',
26784                 href : s
26785             });
26786         });
26787
26788         
26789     },
26790     
26791     removeStylesheets : function()
26792     {
26793         var _this = this;
26794         
26795         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26796             s.remove();
26797         });
26798     },
26799     
26800     setStyle : function(style)
26801     {
26802         Roo.get(this.iframe.contentDocument.head).createChild({
26803             tag : 'style',
26804             type : 'text/css',
26805             html : style
26806         });
26807
26808         return;
26809     }
26810     
26811     // hide stuff that is not compatible
26812     /**
26813      * @event blur
26814      * @hide
26815      */
26816     /**
26817      * @event change
26818      * @hide
26819      */
26820     /**
26821      * @event focus
26822      * @hide
26823      */
26824     /**
26825      * @event specialkey
26826      * @hide
26827      */
26828     /**
26829      * @cfg {String} fieldClass @hide
26830      */
26831     /**
26832      * @cfg {String} focusClass @hide
26833      */
26834     /**
26835      * @cfg {String} autoCreate @hide
26836      */
26837     /**
26838      * @cfg {String} inputType @hide
26839      */
26840     /**
26841      * @cfg {String} invalidClass @hide
26842      */
26843     /**
26844      * @cfg {String} invalidText @hide
26845      */
26846     /**
26847      * @cfg {String} msgFx @hide
26848      */
26849     /**
26850      * @cfg {String} validateOnBlur @hide
26851      */
26852 });
26853
26854 Roo.HtmlEditorCore.white = [
26855         'area', 'br', 'img', 'input', 'hr', 'wbr',
26856         
26857        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26858        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26859        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26860        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26861        'table',   'ul',         'xmp', 
26862        
26863        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26864       'thead',   'tr', 
26865      
26866       'dir', 'menu', 'ol', 'ul', 'dl',
26867        
26868       'embed',  'object'
26869 ];
26870
26871
26872 Roo.HtmlEditorCore.black = [
26873     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26874         'applet', // 
26875         'base',   'basefont', 'bgsound', 'blink',  'body', 
26876         'frame',  'frameset', 'head',    'html',   'ilayer', 
26877         'iframe', 'layer',  'link',     'meta',    'object',   
26878         'script', 'style' ,'title',  'xml' // clean later..
26879 ];
26880 Roo.HtmlEditorCore.clean = [
26881     'script', 'style', 'title', 'xml'
26882 ];
26883 Roo.HtmlEditorCore.remove = [
26884     'font'
26885 ];
26886 // attributes..
26887
26888 Roo.HtmlEditorCore.ablack = [
26889     'on'
26890 ];
26891     
26892 Roo.HtmlEditorCore.aclean = [ 
26893     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26894 ];
26895
26896 // protocols..
26897 Roo.HtmlEditorCore.pwhite= [
26898         'http',  'https',  'mailto'
26899 ];
26900
26901 // white listed style attributes.
26902 Roo.HtmlEditorCore.cwhite= [
26903       //  'text-align', /// default is to allow most things..
26904       
26905          
26906 //        'font-size'//??
26907 ];
26908
26909 // black listed style attributes.
26910 Roo.HtmlEditorCore.cblack= [
26911       //  'font-size' -- this can be set by the project 
26912 ];
26913
26914
26915 Roo.HtmlEditorCore.swapCodes   =[ 
26916     [    8211, "&#8211;" ], 
26917     [    8212, "&#8212;" ], 
26918     [    8216,  "'" ],  
26919     [    8217, "'" ],  
26920     [    8220, '"' ],  
26921     [    8221, '"' ],  
26922     [    8226, "*" ],  
26923     [    8230, "..." ]
26924 ]; 
26925
26926     /*
26927  * - LGPL
26928  *
26929  * HtmlEditor
26930  * 
26931  */
26932
26933 /**
26934  * @class Roo.bootstrap.HtmlEditor
26935  * @extends Roo.bootstrap.TextArea
26936  * Bootstrap HtmlEditor class
26937
26938  * @constructor
26939  * Create a new HtmlEditor
26940  * @param {Object} config The config object
26941  */
26942
26943 Roo.bootstrap.HtmlEditor = function(config){
26944     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26945     if (!this.toolbars) {
26946         this.toolbars = [];
26947     }
26948     
26949     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26950     this.addEvents({
26951             /**
26952              * @event initialize
26953              * Fires when the editor is fully initialized (including the iframe)
26954              * @param {HtmlEditor} this
26955              */
26956             initialize: true,
26957             /**
26958              * @event activate
26959              * Fires when the editor is first receives the focus. Any insertion must wait
26960              * until after this event.
26961              * @param {HtmlEditor} this
26962              */
26963             activate: true,
26964              /**
26965              * @event beforesync
26966              * Fires before the textarea is updated with content from the editor iframe. Return false
26967              * to cancel the sync.
26968              * @param {HtmlEditor} this
26969              * @param {String} html
26970              */
26971             beforesync: true,
26972              /**
26973              * @event beforepush
26974              * Fires before the iframe editor is updated with content from the textarea. Return false
26975              * to cancel the push.
26976              * @param {HtmlEditor} this
26977              * @param {String} html
26978              */
26979             beforepush: true,
26980              /**
26981              * @event sync
26982              * Fires when the textarea is updated with content from the editor iframe.
26983              * @param {HtmlEditor} this
26984              * @param {String} html
26985              */
26986             sync: true,
26987              /**
26988              * @event push
26989              * Fires when the iframe editor is updated with content from the textarea.
26990              * @param {HtmlEditor} this
26991              * @param {String} html
26992              */
26993             push: true,
26994              /**
26995              * @event editmodechange
26996              * Fires when the editor switches edit modes
26997              * @param {HtmlEditor} this
26998              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26999              */
27000             editmodechange: true,
27001             /**
27002              * @event editorevent
27003              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27004              * @param {HtmlEditor} this
27005              */
27006             editorevent: true,
27007             /**
27008              * @event firstfocus
27009              * Fires when on first focus - needed by toolbars..
27010              * @param {HtmlEditor} this
27011              */
27012             firstfocus: true,
27013             /**
27014              * @event autosave
27015              * Auto save the htmlEditor value as a file into Events
27016              * @param {HtmlEditor} this
27017              */
27018             autosave: true,
27019             /**
27020              * @event savedpreview
27021              * preview the saved version of htmlEditor
27022              * @param {HtmlEditor} this
27023              */
27024             savedpreview: true
27025         });
27026 };
27027
27028
27029 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27030     
27031     
27032       /**
27033      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27034      */
27035     toolbars : false,
27036     
27037      /**
27038     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27039     */
27040     btns : [],
27041    
27042      /**
27043      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27044      *                        Roo.resizable.
27045      */
27046     resizable : false,
27047      /**
27048      * @cfg {Number} height (in pixels)
27049      */   
27050     height: 300,
27051    /**
27052      * @cfg {Number} width (in pixels)
27053      */   
27054     width: false,
27055     
27056     /**
27057      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27058      * 
27059      */
27060     stylesheets: false,
27061     
27062     // id of frame..
27063     frameId: false,
27064     
27065     // private properties
27066     validationEvent : false,
27067     deferHeight: true,
27068     initialized : false,
27069     activated : false,
27070     
27071     onFocus : Roo.emptyFn,
27072     iframePad:3,
27073     hideMode:'offsets',
27074     
27075     tbContainer : false,
27076     
27077     bodyCls : '',
27078     
27079     toolbarContainer :function() {
27080         return this.wrap.select('.x-html-editor-tb',true).first();
27081     },
27082
27083     /**
27084      * Protected method that will not generally be called directly. It
27085      * is called when the editor creates its toolbar. Override this method if you need to
27086      * add custom toolbar buttons.
27087      * @param {HtmlEditor} editor
27088      */
27089     createToolbar : function(){
27090         Roo.log('renewing');
27091         Roo.log("create toolbars");
27092         
27093         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27094         this.toolbars[0].render(this.toolbarContainer());
27095         
27096         return;
27097         
27098 //        if (!editor.toolbars || !editor.toolbars.length) {
27099 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27100 //        }
27101 //        
27102 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27103 //            editor.toolbars[i] = Roo.factory(
27104 //                    typeof(editor.toolbars[i]) == 'string' ?
27105 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27106 //                Roo.bootstrap.HtmlEditor);
27107 //            editor.toolbars[i].init(editor);
27108 //        }
27109     },
27110
27111      
27112     // private
27113     onRender : function(ct, position)
27114     {
27115        // Roo.log("Call onRender: " + this.xtype);
27116         var _t = this;
27117         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27118       
27119         this.wrap = this.inputEl().wrap({
27120             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27121         });
27122         
27123         this.editorcore.onRender(ct, position);
27124          
27125         if (this.resizable) {
27126             this.resizeEl = new Roo.Resizable(this.wrap, {
27127                 pinned : true,
27128                 wrap: true,
27129                 dynamic : true,
27130                 minHeight : this.height,
27131                 height: this.height,
27132                 handles : this.resizable,
27133                 width: this.width,
27134                 listeners : {
27135                     resize : function(r, w, h) {
27136                         _t.onResize(w,h); // -something
27137                     }
27138                 }
27139             });
27140             
27141         }
27142         this.createToolbar(this);
27143        
27144         
27145         if(!this.width && this.resizable){
27146             this.setSize(this.wrap.getSize());
27147         }
27148         if (this.resizeEl) {
27149             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27150             // should trigger onReize..
27151         }
27152         
27153     },
27154
27155     // private
27156     onResize : function(w, h)
27157     {
27158         Roo.log('resize: ' +w + ',' + h );
27159         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27160         var ew = false;
27161         var eh = false;
27162         
27163         if(this.inputEl() ){
27164             if(typeof w == 'number'){
27165                 var aw = w - this.wrap.getFrameWidth('lr');
27166                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27167                 ew = aw;
27168             }
27169             if(typeof h == 'number'){
27170                  var tbh = -11;  // fixme it needs to tool bar size!
27171                 for (var i =0; i < this.toolbars.length;i++) {
27172                     // fixme - ask toolbars for heights?
27173                     tbh += this.toolbars[i].el.getHeight();
27174                     //if (this.toolbars[i].footer) {
27175                     //    tbh += this.toolbars[i].footer.el.getHeight();
27176                     //}
27177                 }
27178               
27179                 
27180                 
27181                 
27182                 
27183                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27184                 ah -= 5; // knock a few pixes off for look..
27185                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27186                 var eh = ah;
27187             }
27188         }
27189         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27190         this.editorcore.onResize(ew,eh);
27191         
27192     },
27193
27194     /**
27195      * Toggles the editor between standard and source edit mode.
27196      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27197      */
27198     toggleSourceEdit : function(sourceEditMode)
27199     {
27200         this.editorcore.toggleSourceEdit(sourceEditMode);
27201         
27202         if(this.editorcore.sourceEditMode){
27203             Roo.log('editor - showing textarea');
27204             
27205 //            Roo.log('in');
27206 //            Roo.log(this.syncValue());
27207             this.syncValue();
27208             this.inputEl().removeClass(['hide', 'x-hidden']);
27209             this.inputEl().dom.removeAttribute('tabIndex');
27210             this.inputEl().focus();
27211         }else{
27212             Roo.log('editor - hiding textarea');
27213 //            Roo.log('out')
27214 //            Roo.log(this.pushValue()); 
27215             this.pushValue();
27216             
27217             this.inputEl().addClass(['hide', 'x-hidden']);
27218             this.inputEl().dom.setAttribute('tabIndex', -1);
27219             //this.deferFocus();
27220         }
27221          
27222         if(this.resizable){
27223             this.setSize(this.wrap.getSize());
27224         }
27225         
27226         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27227     },
27228  
27229     // private (for BoxComponent)
27230     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27231
27232     // private (for BoxComponent)
27233     getResizeEl : function(){
27234         return this.wrap;
27235     },
27236
27237     // private (for BoxComponent)
27238     getPositionEl : function(){
27239         return this.wrap;
27240     },
27241
27242     // private
27243     initEvents : function(){
27244         this.originalValue = this.getValue();
27245     },
27246
27247 //    /**
27248 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27249 //     * @method
27250 //     */
27251 //    markInvalid : Roo.emptyFn,
27252 //    /**
27253 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27254 //     * @method
27255 //     */
27256 //    clearInvalid : Roo.emptyFn,
27257
27258     setValue : function(v){
27259         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27260         this.editorcore.pushValue();
27261     },
27262
27263      
27264     // private
27265     deferFocus : function(){
27266         this.focus.defer(10, this);
27267     },
27268
27269     // doc'ed in Field
27270     focus : function(){
27271         this.editorcore.focus();
27272         
27273     },
27274       
27275
27276     // private
27277     onDestroy : function(){
27278         
27279         
27280         
27281         if(this.rendered){
27282             
27283             for (var i =0; i < this.toolbars.length;i++) {
27284                 // fixme - ask toolbars for heights?
27285                 this.toolbars[i].onDestroy();
27286             }
27287             
27288             this.wrap.dom.innerHTML = '';
27289             this.wrap.remove();
27290         }
27291     },
27292
27293     // private
27294     onFirstFocus : function(){
27295         //Roo.log("onFirstFocus");
27296         this.editorcore.onFirstFocus();
27297          for (var i =0; i < this.toolbars.length;i++) {
27298             this.toolbars[i].onFirstFocus();
27299         }
27300         
27301     },
27302     
27303     // private
27304     syncValue : function()
27305     {   
27306         this.editorcore.syncValue();
27307     },
27308     
27309     pushValue : function()
27310     {   
27311         this.editorcore.pushValue();
27312     }
27313      
27314     
27315     // hide stuff that is not compatible
27316     /**
27317      * @event blur
27318      * @hide
27319      */
27320     /**
27321      * @event change
27322      * @hide
27323      */
27324     /**
27325      * @event focus
27326      * @hide
27327      */
27328     /**
27329      * @event specialkey
27330      * @hide
27331      */
27332     /**
27333      * @cfg {String} fieldClass @hide
27334      */
27335     /**
27336      * @cfg {String} focusClass @hide
27337      */
27338     /**
27339      * @cfg {String} autoCreate @hide
27340      */
27341     /**
27342      * @cfg {String} inputType @hide
27343      */
27344      
27345     /**
27346      * @cfg {String} invalidText @hide
27347      */
27348     /**
27349      * @cfg {String} msgFx @hide
27350      */
27351     /**
27352      * @cfg {String} validateOnBlur @hide
27353      */
27354 });
27355  
27356     
27357    
27358    
27359    
27360       
27361 Roo.namespace('Roo.bootstrap.htmleditor');
27362 /**
27363  * @class Roo.bootstrap.HtmlEditorToolbar1
27364  * Basic Toolbar
27365  * 
27366  * @example
27367  * Usage:
27368  *
27369  new Roo.bootstrap.HtmlEditor({
27370     ....
27371     toolbars : [
27372         new Roo.bootstrap.HtmlEditorToolbar1({
27373             disable : { fonts: 1 , format: 1, ..., ... , ...],
27374             btns : [ .... ]
27375         })
27376     }
27377      
27378  * 
27379  * @cfg {Object} disable List of elements to disable..
27380  * @cfg {Array} btns List of additional buttons.
27381  * 
27382  * 
27383  * NEEDS Extra CSS? 
27384  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27385  */
27386  
27387 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27388 {
27389     
27390     Roo.apply(this, config);
27391     
27392     // default disabled, based on 'good practice'..
27393     this.disable = this.disable || {};
27394     Roo.applyIf(this.disable, {
27395         fontSize : true,
27396         colors : true,
27397         specialElements : true
27398     });
27399     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27400     
27401     this.editor = config.editor;
27402     this.editorcore = config.editor.editorcore;
27403     
27404     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27405     
27406     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27407     // dont call parent... till later.
27408 }
27409 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27410      
27411     bar : true,
27412     
27413     editor : false,
27414     editorcore : false,
27415     
27416     
27417     formats : [
27418         "p" ,  
27419         "h1","h2","h3","h4","h5","h6", 
27420         "pre", "code", 
27421         "abbr", "acronym", "address", "cite", "samp", "var",
27422         'div','span'
27423     ],
27424     
27425     onRender : function(ct, position)
27426     {
27427        // Roo.log("Call onRender: " + this.xtype);
27428         
27429        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27430        Roo.log(this.el);
27431        this.el.dom.style.marginBottom = '0';
27432        var _this = this;
27433        var editorcore = this.editorcore;
27434        var editor= this.editor;
27435        
27436        var children = [];
27437        var btn = function(id,cmd , toggle, handler, html){
27438        
27439             var  event = toggle ? 'toggle' : 'click';
27440        
27441             var a = {
27442                 size : 'sm',
27443                 xtype: 'Button',
27444                 xns: Roo.bootstrap,
27445                 //glyphicon : id,
27446                 fa: id,
27447                 cmd : id || cmd,
27448                 enableToggle:toggle !== false,
27449                 html : html || '',
27450                 pressed : toggle ? false : null,
27451                 listeners : {}
27452             };
27453             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27454                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27455             };
27456             children.push(a);
27457             return a;
27458        }
27459        
27460     //    var cb_box = function...
27461         
27462         var style = {
27463                 xtype: 'Button',
27464                 size : 'sm',
27465                 xns: Roo.bootstrap,
27466                 fa : 'font',
27467                 //html : 'submit'
27468                 menu : {
27469                     xtype: 'Menu',
27470                     xns: Roo.bootstrap,
27471                     items:  []
27472                 }
27473         };
27474         Roo.each(this.formats, function(f) {
27475             style.menu.items.push({
27476                 xtype :'MenuItem',
27477                 xns: Roo.bootstrap,
27478                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27479                 tagname : f,
27480                 listeners : {
27481                     click : function()
27482                     {
27483                         editorcore.insertTag(this.tagname);
27484                         editor.focus();
27485                     }
27486                 }
27487                 
27488             });
27489         });
27490         children.push(style);   
27491         
27492         btn('bold',false,true);
27493         btn('italic',false,true);
27494         btn('align-left', 'justifyleft',true);
27495         btn('align-center', 'justifycenter',true);
27496         btn('align-right' , 'justifyright',true);
27497         btn('link', false, false, function(btn) {
27498             //Roo.log("create link?");
27499             var url = prompt(this.createLinkText, this.defaultLinkValue);
27500             if(url && url != 'http:/'+'/'){
27501                 this.editorcore.relayCmd('createlink', url);
27502             }
27503         }),
27504         btn('list','insertunorderedlist',true);
27505         btn('pencil', false,true, function(btn){
27506                 Roo.log(this);
27507                 this.toggleSourceEdit(btn.pressed);
27508         });
27509         
27510         if (this.editor.btns.length > 0) {
27511             for (var i = 0; i<this.editor.btns.length; i++) {
27512                 children.push(this.editor.btns[i]);
27513             }
27514         }
27515         
27516         /*
27517         var cog = {
27518                 xtype: 'Button',
27519                 size : 'sm',
27520                 xns: Roo.bootstrap,
27521                 glyphicon : 'cog',
27522                 //html : 'submit'
27523                 menu : {
27524                     xtype: 'Menu',
27525                     xns: Roo.bootstrap,
27526                     items:  []
27527                 }
27528         };
27529         
27530         cog.menu.items.push({
27531             xtype :'MenuItem',
27532             xns: Roo.bootstrap,
27533             html : Clean styles,
27534             tagname : f,
27535             listeners : {
27536                 click : function()
27537                 {
27538                     editorcore.insertTag(this.tagname);
27539                     editor.focus();
27540                 }
27541             }
27542             
27543         });
27544        */
27545         
27546          
27547        this.xtype = 'NavSimplebar';
27548         
27549         for(var i=0;i< children.length;i++) {
27550             
27551             this.buttons.add(this.addxtypeChild(children[i]));
27552             
27553         }
27554         
27555         editor.on('editorevent', this.updateToolbar, this);
27556     },
27557     onBtnClick : function(id)
27558     {
27559        this.editorcore.relayCmd(id);
27560        this.editorcore.focus();
27561     },
27562     
27563     /**
27564      * Protected method that will not generally be called directly. It triggers
27565      * a toolbar update by reading the markup state of the current selection in the editor.
27566      */
27567     updateToolbar: function(){
27568
27569         if(!this.editorcore.activated){
27570             this.editor.onFirstFocus(); // is this neeed?
27571             return;
27572         }
27573
27574         var btns = this.buttons; 
27575         var doc = this.editorcore.doc;
27576         btns.get('bold').setActive(doc.queryCommandState('bold'));
27577         btns.get('italic').setActive(doc.queryCommandState('italic'));
27578         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27579         
27580         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27581         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27582         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27583         
27584         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27585         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27586          /*
27587         
27588         var ans = this.editorcore.getAllAncestors();
27589         if (this.formatCombo) {
27590             
27591             
27592             var store = this.formatCombo.store;
27593             this.formatCombo.setValue("");
27594             for (var i =0; i < ans.length;i++) {
27595                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27596                     // select it..
27597                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27598                     break;
27599                 }
27600             }
27601         }
27602         
27603         
27604         
27605         // hides menus... - so this cant be on a menu...
27606         Roo.bootstrap.MenuMgr.hideAll();
27607         */
27608         Roo.bootstrap.MenuMgr.hideAll();
27609         //this.editorsyncValue();
27610     },
27611     onFirstFocus: function() {
27612         this.buttons.each(function(item){
27613            item.enable();
27614         });
27615     },
27616     toggleSourceEdit : function(sourceEditMode){
27617         
27618           
27619         if(sourceEditMode){
27620             Roo.log("disabling buttons");
27621            this.buttons.each( function(item){
27622                 if(item.cmd != 'pencil'){
27623                     item.disable();
27624                 }
27625             });
27626           
27627         }else{
27628             Roo.log("enabling buttons");
27629             if(this.editorcore.initialized){
27630                 this.buttons.each( function(item){
27631                     item.enable();
27632                 });
27633             }
27634             
27635         }
27636         Roo.log("calling toggole on editor");
27637         // tell the editor that it's been pressed..
27638         this.editor.toggleSourceEdit(sourceEditMode);
27639        
27640     }
27641 });
27642
27643
27644
27645
27646  
27647 /*
27648  * - LGPL
27649  */
27650
27651 /**
27652  * @class Roo.bootstrap.Markdown
27653  * @extends Roo.bootstrap.TextArea
27654  * Bootstrap Showdown editable area
27655  * @cfg {string} content
27656  * 
27657  * @constructor
27658  * Create a new Showdown
27659  */
27660
27661 Roo.bootstrap.Markdown = function(config){
27662     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27663    
27664 };
27665
27666 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27667     
27668     editing :false,
27669     
27670     initEvents : function()
27671     {
27672         
27673         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27674         this.markdownEl = this.el.createChild({
27675             cls : 'roo-markdown-area'
27676         });
27677         this.inputEl().addClass('d-none');
27678         if (this.getValue() == '') {
27679             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27680             
27681         } else {
27682             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27683         }
27684         this.markdownEl.on('click', this.toggleTextEdit, this);
27685         this.on('blur', this.toggleTextEdit, this);
27686         this.on('specialkey', this.resizeTextArea, this);
27687     },
27688     
27689     toggleTextEdit : function()
27690     {
27691         var sh = this.markdownEl.getHeight();
27692         this.inputEl().addClass('d-none');
27693         this.markdownEl.addClass('d-none');
27694         if (!this.editing) {
27695             // show editor?
27696             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27697             this.inputEl().removeClass('d-none');
27698             this.inputEl().focus();
27699             this.editing = true;
27700             return;
27701         }
27702         // show showdown...
27703         this.updateMarkdown();
27704         this.markdownEl.removeClass('d-none');
27705         this.editing = false;
27706         return;
27707     },
27708     updateMarkdown : function()
27709     {
27710         if (this.getValue() == '') {
27711             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27712             return;
27713         }
27714  
27715         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27716     },
27717     
27718     resizeTextArea: function () {
27719         
27720         var sh = 100;
27721         Roo.log([sh, this.getValue().split("\n").length * 30]);
27722         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27723     },
27724     setValue : function(val)
27725     {
27726         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27727         if (!this.editing) {
27728             this.updateMarkdown();
27729         }
27730         
27731     },
27732     focus : function()
27733     {
27734         if (!this.editing) {
27735             this.toggleTextEdit();
27736         }
27737         
27738     }
27739
27740
27741 });/*
27742  * Based on:
27743  * Ext JS Library 1.1.1
27744  * Copyright(c) 2006-2007, Ext JS, LLC.
27745  *
27746  * Originally Released Under LGPL - original licence link has changed is not relivant.
27747  *
27748  * Fork - LGPL
27749  * <script type="text/javascript">
27750  */
27751  
27752 /**
27753  * @class Roo.bootstrap.PagingToolbar
27754  * @extends Roo.bootstrap.NavSimplebar
27755  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27756  * @constructor
27757  * Create a new PagingToolbar
27758  * @param {Object} config The config object
27759  * @param {Roo.data.Store} store
27760  */
27761 Roo.bootstrap.PagingToolbar = function(config)
27762 {
27763     // old args format still supported... - xtype is prefered..
27764         // created from xtype...
27765     
27766     this.ds = config.dataSource;
27767     
27768     if (config.store && !this.ds) {
27769         this.store= Roo.factory(config.store, Roo.data);
27770         this.ds = this.store;
27771         this.ds.xmodule = this.xmodule || false;
27772     }
27773     
27774     this.toolbarItems = [];
27775     if (config.items) {
27776         this.toolbarItems = config.items;
27777     }
27778     
27779     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27780     
27781     this.cursor = 0;
27782     
27783     if (this.ds) { 
27784         this.bind(this.ds);
27785     }
27786     
27787     if (Roo.bootstrap.version == 4) {
27788         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27789     } else {
27790         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27791     }
27792     
27793 };
27794
27795 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27796     /**
27797      * @cfg {Roo.data.Store} dataSource
27798      * The underlying data store providing the paged data
27799      */
27800     /**
27801      * @cfg {String/HTMLElement/Element} container
27802      * container The id or element that will contain the toolbar
27803      */
27804     /**
27805      * @cfg {Boolean} displayInfo
27806      * True to display the displayMsg (defaults to false)
27807      */
27808     /**
27809      * @cfg {Number} pageSize
27810      * The number of records to display per page (defaults to 20)
27811      */
27812     pageSize: 20,
27813     /**
27814      * @cfg {String} displayMsg
27815      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27816      */
27817     displayMsg : 'Displaying {0} - {1} of {2}',
27818     /**
27819      * @cfg {String} emptyMsg
27820      * The message to display when no records are found (defaults to "No data to display")
27821      */
27822     emptyMsg : 'No data to display',
27823     /**
27824      * Customizable piece of the default paging text (defaults to "Page")
27825      * @type String
27826      */
27827     beforePageText : "Page",
27828     /**
27829      * Customizable piece of the default paging text (defaults to "of %0")
27830      * @type String
27831      */
27832     afterPageText : "of {0}",
27833     /**
27834      * Customizable piece of the default paging text (defaults to "First Page")
27835      * @type String
27836      */
27837     firstText : "First Page",
27838     /**
27839      * Customizable piece of the default paging text (defaults to "Previous Page")
27840      * @type String
27841      */
27842     prevText : "Previous Page",
27843     /**
27844      * Customizable piece of the default paging text (defaults to "Next Page")
27845      * @type String
27846      */
27847     nextText : "Next Page",
27848     /**
27849      * Customizable piece of the default paging text (defaults to "Last Page")
27850      * @type String
27851      */
27852     lastText : "Last Page",
27853     /**
27854      * Customizable piece of the default paging text (defaults to "Refresh")
27855      * @type String
27856      */
27857     refreshText : "Refresh",
27858
27859     buttons : false,
27860     // private
27861     onRender : function(ct, position) 
27862     {
27863         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27864         this.navgroup.parentId = this.id;
27865         this.navgroup.onRender(this.el, null);
27866         // add the buttons to the navgroup
27867         
27868         if(this.displayInfo){
27869             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27870             this.displayEl = this.el.select('.x-paging-info', true).first();
27871 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27872 //            this.displayEl = navel.el.select('span',true).first();
27873         }
27874         
27875         var _this = this;
27876         
27877         if(this.buttons){
27878             Roo.each(_this.buttons, function(e){ // this might need to use render????
27879                Roo.factory(e).render(_this.el);
27880             });
27881         }
27882             
27883         Roo.each(_this.toolbarItems, function(e) {
27884             _this.navgroup.addItem(e);
27885         });
27886         
27887         
27888         this.first = this.navgroup.addItem({
27889             tooltip: this.firstText,
27890             cls: "prev btn-outline-secondary",
27891             html : ' <i class="fa fa-step-backward"></i>',
27892             disabled: true,
27893             preventDefault: true,
27894             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27895         });
27896         
27897         this.prev =  this.navgroup.addItem({
27898             tooltip: this.prevText,
27899             cls: "prev btn-outline-secondary",
27900             html : ' <i class="fa fa-backward"></i>',
27901             disabled: true,
27902             preventDefault: true,
27903             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27904         });
27905     //this.addSeparator();
27906         
27907         
27908         var field = this.navgroup.addItem( {
27909             tagtype : 'span',
27910             cls : 'x-paging-position  btn-outline-secondary',
27911              disabled: true,
27912             html : this.beforePageText  +
27913                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27914                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27915          } ); //?? escaped?
27916         
27917         this.field = field.el.select('input', true).first();
27918         this.field.on("keydown", this.onPagingKeydown, this);
27919         this.field.on("focus", function(){this.dom.select();});
27920     
27921     
27922         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27923         //this.field.setHeight(18);
27924         //this.addSeparator();
27925         this.next = this.navgroup.addItem({
27926             tooltip: this.nextText,
27927             cls: "next btn-outline-secondary",
27928             html : ' <i class="fa fa-forward"></i>',
27929             disabled: true,
27930             preventDefault: true,
27931             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27932         });
27933         this.last = this.navgroup.addItem({
27934             tooltip: this.lastText,
27935             html : ' <i class="fa fa-step-forward"></i>',
27936             cls: "next btn-outline-secondary",
27937             disabled: true,
27938             preventDefault: true,
27939             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27940         });
27941     //this.addSeparator();
27942         this.loading = this.navgroup.addItem({
27943             tooltip: this.refreshText,
27944             cls: "btn-outline-secondary",
27945             html : ' <i class="fa fa-refresh"></i>',
27946             preventDefault: true,
27947             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27948         });
27949         
27950     },
27951
27952     // private
27953     updateInfo : function(){
27954         if(this.displayEl){
27955             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27956             var msg = count == 0 ?
27957                 this.emptyMsg :
27958                 String.format(
27959                     this.displayMsg,
27960                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27961                 );
27962             this.displayEl.update(msg);
27963         }
27964     },
27965
27966     // private
27967     onLoad : function(ds, r, o)
27968     {
27969         this.cursor = o.params && o.params.start ? o.params.start : 0;
27970         
27971         var d = this.getPageData(),
27972             ap = d.activePage,
27973             ps = d.pages;
27974         
27975         
27976         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27977         this.field.dom.value = ap;
27978         this.first.setDisabled(ap == 1);
27979         this.prev.setDisabled(ap == 1);
27980         this.next.setDisabled(ap == ps);
27981         this.last.setDisabled(ap == ps);
27982         this.loading.enable();
27983         this.updateInfo();
27984     },
27985
27986     // private
27987     getPageData : function(){
27988         var total = this.ds.getTotalCount();
27989         return {
27990             total : total,
27991             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27992             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27993         };
27994     },
27995
27996     // private
27997     onLoadError : function(){
27998         this.loading.enable();
27999     },
28000
28001     // private
28002     onPagingKeydown : function(e){
28003         var k = e.getKey();
28004         var d = this.getPageData();
28005         if(k == e.RETURN){
28006             var v = this.field.dom.value, pageNum;
28007             if(!v || isNaN(pageNum = parseInt(v, 10))){
28008                 this.field.dom.value = d.activePage;
28009                 return;
28010             }
28011             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28012             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28013             e.stopEvent();
28014         }
28015         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))
28016         {
28017           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28018           this.field.dom.value = pageNum;
28019           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28020           e.stopEvent();
28021         }
28022         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28023         {
28024           var v = this.field.dom.value, pageNum; 
28025           var increment = (e.shiftKey) ? 10 : 1;
28026           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28027                 increment *= -1;
28028           }
28029           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28030             this.field.dom.value = d.activePage;
28031             return;
28032           }
28033           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28034           {
28035             this.field.dom.value = parseInt(v, 10) + increment;
28036             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28037             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28038           }
28039           e.stopEvent();
28040         }
28041     },
28042
28043     // private
28044     beforeLoad : function(){
28045         if(this.loading){
28046             this.loading.disable();
28047         }
28048     },
28049
28050     // private
28051     onClick : function(which){
28052         
28053         var ds = this.ds;
28054         if (!ds) {
28055             return;
28056         }
28057         
28058         switch(which){
28059             case "first":
28060                 ds.load({params:{start: 0, limit: this.pageSize}});
28061             break;
28062             case "prev":
28063                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28064             break;
28065             case "next":
28066                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28067             break;
28068             case "last":
28069                 var total = ds.getTotalCount();
28070                 var extra = total % this.pageSize;
28071                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28072                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28073             break;
28074             case "refresh":
28075                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28076             break;
28077         }
28078     },
28079
28080     /**
28081      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28082      * @param {Roo.data.Store} store The data store to unbind
28083      */
28084     unbind : function(ds){
28085         ds.un("beforeload", this.beforeLoad, this);
28086         ds.un("load", this.onLoad, this);
28087         ds.un("loadexception", this.onLoadError, this);
28088         ds.un("remove", this.updateInfo, this);
28089         ds.un("add", this.updateInfo, this);
28090         this.ds = undefined;
28091     },
28092
28093     /**
28094      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28095      * @param {Roo.data.Store} store The data store to bind
28096      */
28097     bind : function(ds){
28098         ds.on("beforeload", this.beforeLoad, this);
28099         ds.on("load", this.onLoad, this);
28100         ds.on("loadexception", this.onLoadError, this);
28101         ds.on("remove", this.updateInfo, this);
28102         ds.on("add", this.updateInfo, this);
28103         this.ds = ds;
28104     }
28105 });/*
28106  * - LGPL
28107  *
28108  * element
28109  * 
28110  */
28111
28112 /**
28113  * @class Roo.bootstrap.MessageBar
28114  * @extends Roo.bootstrap.Component
28115  * Bootstrap MessageBar class
28116  * @cfg {String} html contents of the MessageBar
28117  * @cfg {String} weight (info | success | warning | danger) default info
28118  * @cfg {String} beforeClass insert the bar before the given class
28119  * @cfg {Boolean} closable (true | false) default false
28120  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28121  * 
28122  * @constructor
28123  * Create a new Element
28124  * @param {Object} config The config object
28125  */
28126
28127 Roo.bootstrap.MessageBar = function(config){
28128     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28129 };
28130
28131 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28132     
28133     html: '',
28134     weight: 'info',
28135     closable: false,
28136     fixed: false,
28137     beforeClass: 'bootstrap-sticky-wrap',
28138     
28139     getAutoCreate : function(){
28140         
28141         var cfg = {
28142             tag: 'div',
28143             cls: 'alert alert-dismissable alert-' + this.weight,
28144             cn: [
28145                 {
28146                     tag: 'span',
28147                     cls: 'message',
28148                     html: this.html || ''
28149                 }
28150             ]
28151         };
28152         
28153         if(this.fixed){
28154             cfg.cls += ' alert-messages-fixed';
28155         }
28156         
28157         if(this.closable){
28158             cfg.cn.push({
28159                 tag: 'button',
28160                 cls: 'close',
28161                 html: 'x'
28162             });
28163         }
28164         
28165         return cfg;
28166     },
28167     
28168     onRender : function(ct, position)
28169     {
28170         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28171         
28172         if(!this.el){
28173             var cfg = Roo.apply({},  this.getAutoCreate());
28174             cfg.id = Roo.id();
28175             
28176             if (this.cls) {
28177                 cfg.cls += ' ' + this.cls;
28178             }
28179             if (this.style) {
28180                 cfg.style = this.style;
28181             }
28182             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28183             
28184             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28185         }
28186         
28187         this.el.select('>button.close').on('click', this.hide, this);
28188         
28189     },
28190     
28191     show : function()
28192     {
28193         if (!this.rendered) {
28194             this.render();
28195         }
28196         
28197         this.el.show();
28198         
28199         this.fireEvent('show', this);
28200         
28201     },
28202     
28203     hide : function()
28204     {
28205         if (!this.rendered) {
28206             this.render();
28207         }
28208         
28209         this.el.hide();
28210         
28211         this.fireEvent('hide', this);
28212     },
28213     
28214     update : function()
28215     {
28216 //        var e = this.el.dom.firstChild;
28217 //        
28218 //        if(this.closable){
28219 //            e = e.nextSibling;
28220 //        }
28221 //        
28222 //        e.data = this.html || '';
28223
28224         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28225     }
28226    
28227 });
28228
28229  
28230
28231      /*
28232  * - LGPL
28233  *
28234  * Graph
28235  * 
28236  */
28237
28238
28239 /**
28240  * @class Roo.bootstrap.Graph
28241  * @extends Roo.bootstrap.Component
28242  * Bootstrap Graph class
28243 > Prameters
28244  -sm {number} sm 4
28245  -md {number} md 5
28246  @cfg {String} graphtype  bar | vbar | pie
28247  @cfg {number} g_x coodinator | centre x (pie)
28248  @cfg {number} g_y coodinator | centre y (pie)
28249  @cfg {number} g_r radius (pie)
28250  @cfg {number} g_height height of the chart (respected by all elements in the set)
28251  @cfg {number} g_width width of the chart (respected by all elements in the set)
28252  @cfg {Object} title The title of the chart
28253     
28254  -{Array}  values
28255  -opts (object) options for the chart 
28256      o {
28257      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28258      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28259      o vgutter (number)
28260      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.
28261      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28262      o to
28263      o stretch (boolean)
28264      o }
28265  -opts (object) options for the pie
28266      o{
28267      o cut
28268      o startAngle (number)
28269      o endAngle (number)
28270      } 
28271  *
28272  * @constructor
28273  * Create a new Input
28274  * @param {Object} config The config object
28275  */
28276
28277 Roo.bootstrap.Graph = function(config){
28278     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28279     
28280     this.addEvents({
28281         // img events
28282         /**
28283          * @event click
28284          * The img click event for the img.
28285          * @param {Roo.EventObject} e
28286          */
28287         "click" : true
28288     });
28289 };
28290
28291 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28292     
28293     sm: 4,
28294     md: 5,
28295     graphtype: 'bar',
28296     g_height: 250,
28297     g_width: 400,
28298     g_x: 50,
28299     g_y: 50,
28300     g_r: 30,
28301     opts:{
28302         //g_colors: this.colors,
28303         g_type: 'soft',
28304         g_gutter: '20%'
28305
28306     },
28307     title : false,
28308
28309     getAutoCreate : function(){
28310         
28311         var cfg = {
28312             tag: 'div',
28313             html : null
28314         };
28315         
28316         
28317         return  cfg;
28318     },
28319
28320     onRender : function(ct,position){
28321         
28322         
28323         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28324         
28325         if (typeof(Raphael) == 'undefined') {
28326             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28327             return;
28328         }
28329         
28330         this.raphael = Raphael(this.el.dom);
28331         
28332                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28333                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28334                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28335                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28336                 /*
28337                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28338                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28339                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28340                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28341                 
28342                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28343                 r.barchart(330, 10, 300, 220, data1);
28344                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28345                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28346                 */
28347                 
28348                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28349                 // r.barchart(30, 30, 560, 250,  xdata, {
28350                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28351                 //     axis : "0 0 1 1",
28352                 //     axisxlabels :  xdata
28353                 //     //yvalues : cols,
28354                    
28355                 // });
28356 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28357 //        
28358 //        this.load(null,xdata,{
28359 //                axis : "0 0 1 1",
28360 //                axisxlabels :  xdata
28361 //                });
28362
28363     },
28364
28365     load : function(graphtype,xdata,opts)
28366     {
28367         this.raphael.clear();
28368         if(!graphtype) {
28369             graphtype = this.graphtype;
28370         }
28371         if(!opts){
28372             opts = this.opts;
28373         }
28374         var r = this.raphael,
28375             fin = function () {
28376                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28377             },
28378             fout = function () {
28379                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28380             },
28381             pfin = function() {
28382                 this.sector.stop();
28383                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28384
28385                 if (this.label) {
28386                     this.label[0].stop();
28387                     this.label[0].attr({ r: 7.5 });
28388                     this.label[1].attr({ "font-weight": 800 });
28389                 }
28390             },
28391             pfout = function() {
28392                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28393
28394                 if (this.label) {
28395                     this.label[0].animate({ r: 5 }, 500, "bounce");
28396                     this.label[1].attr({ "font-weight": 400 });
28397                 }
28398             };
28399
28400         switch(graphtype){
28401             case 'bar':
28402                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28403                 break;
28404             case 'hbar':
28405                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28406                 break;
28407             case 'pie':
28408 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28409 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28410 //            
28411                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28412                 
28413                 break;
28414
28415         }
28416         
28417         if(this.title){
28418             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28419         }
28420         
28421     },
28422     
28423     setTitle: function(o)
28424     {
28425         this.title = o;
28426     },
28427     
28428     initEvents: function() {
28429         
28430         if(!this.href){
28431             this.el.on('click', this.onClick, this);
28432         }
28433     },
28434     
28435     onClick : function(e)
28436     {
28437         Roo.log('img onclick');
28438         this.fireEvent('click', this, e);
28439     }
28440    
28441 });
28442
28443  
28444 /*
28445  * - LGPL
28446  *
28447  * numberBox
28448  * 
28449  */
28450 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28451
28452 /**
28453  * @class Roo.bootstrap.dash.NumberBox
28454  * @extends Roo.bootstrap.Component
28455  * Bootstrap NumberBox class
28456  * @cfg {String} headline Box headline
28457  * @cfg {String} content Box content
28458  * @cfg {String} icon Box icon
28459  * @cfg {String} footer Footer text
28460  * @cfg {String} fhref Footer href
28461  * 
28462  * @constructor
28463  * Create a new NumberBox
28464  * @param {Object} config The config object
28465  */
28466
28467
28468 Roo.bootstrap.dash.NumberBox = function(config){
28469     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28470     
28471 };
28472
28473 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28474     
28475     headline : '',
28476     content : '',
28477     icon : '',
28478     footer : '',
28479     fhref : '',
28480     ficon : '',
28481     
28482     getAutoCreate : function(){
28483         
28484         var cfg = {
28485             tag : 'div',
28486             cls : 'small-box ',
28487             cn : [
28488                 {
28489                     tag : 'div',
28490                     cls : 'inner',
28491                     cn :[
28492                         {
28493                             tag : 'h3',
28494                             cls : 'roo-headline',
28495                             html : this.headline
28496                         },
28497                         {
28498                             tag : 'p',
28499                             cls : 'roo-content',
28500                             html : this.content
28501                         }
28502                     ]
28503                 }
28504             ]
28505         };
28506         
28507         if(this.icon){
28508             cfg.cn.push({
28509                 tag : 'div',
28510                 cls : 'icon',
28511                 cn :[
28512                     {
28513                         tag : 'i',
28514                         cls : 'ion ' + this.icon
28515                     }
28516                 ]
28517             });
28518         }
28519         
28520         if(this.footer){
28521             var footer = {
28522                 tag : 'a',
28523                 cls : 'small-box-footer',
28524                 href : this.fhref || '#',
28525                 html : this.footer
28526             };
28527             
28528             cfg.cn.push(footer);
28529             
28530         }
28531         
28532         return  cfg;
28533     },
28534
28535     onRender : function(ct,position){
28536         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28537
28538
28539        
28540                 
28541     },
28542
28543     setHeadline: function (value)
28544     {
28545         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28546     },
28547     
28548     setFooter: function (value, href)
28549     {
28550         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28551         
28552         if(href){
28553             this.el.select('a.small-box-footer',true).first().attr('href', href);
28554         }
28555         
28556     },
28557
28558     setContent: function (value)
28559     {
28560         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28561     },
28562
28563     initEvents: function() 
28564     {   
28565         
28566     }
28567     
28568 });
28569
28570  
28571 /*
28572  * - LGPL
28573  *
28574  * TabBox
28575  * 
28576  */
28577 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28578
28579 /**
28580  * @class Roo.bootstrap.dash.TabBox
28581  * @extends Roo.bootstrap.Component
28582  * Bootstrap TabBox class
28583  * @cfg {String} title Title of the TabBox
28584  * @cfg {String} icon Icon of the TabBox
28585  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28586  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28587  * 
28588  * @constructor
28589  * Create a new TabBox
28590  * @param {Object} config The config object
28591  */
28592
28593
28594 Roo.bootstrap.dash.TabBox = function(config){
28595     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28596     this.addEvents({
28597         // raw events
28598         /**
28599          * @event addpane
28600          * When a pane is added
28601          * @param {Roo.bootstrap.dash.TabPane} pane
28602          */
28603         "addpane" : true,
28604         /**
28605          * @event activatepane
28606          * When a pane is activated
28607          * @param {Roo.bootstrap.dash.TabPane} pane
28608          */
28609         "activatepane" : true
28610         
28611          
28612     });
28613     
28614     this.panes = [];
28615 };
28616
28617 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28618
28619     title : '',
28620     icon : false,
28621     showtabs : true,
28622     tabScrollable : false,
28623     
28624     getChildContainer : function()
28625     {
28626         return this.el.select('.tab-content', true).first();
28627     },
28628     
28629     getAutoCreate : function(){
28630         
28631         var header = {
28632             tag: 'li',
28633             cls: 'pull-left header',
28634             html: this.title,
28635             cn : []
28636         };
28637         
28638         if(this.icon){
28639             header.cn.push({
28640                 tag: 'i',
28641                 cls: 'fa ' + this.icon
28642             });
28643         }
28644         
28645         var h = {
28646             tag: 'ul',
28647             cls: 'nav nav-tabs pull-right',
28648             cn: [
28649                 header
28650             ]
28651         };
28652         
28653         if(this.tabScrollable){
28654             h = {
28655                 tag: 'div',
28656                 cls: 'tab-header',
28657                 cn: [
28658                     {
28659                         tag: 'ul',
28660                         cls: 'nav nav-tabs pull-right',
28661                         cn: [
28662                             header
28663                         ]
28664                     }
28665                 ]
28666             };
28667         }
28668         
28669         var cfg = {
28670             tag: 'div',
28671             cls: 'nav-tabs-custom',
28672             cn: [
28673                 h,
28674                 {
28675                     tag: 'div',
28676                     cls: 'tab-content no-padding',
28677                     cn: []
28678                 }
28679             ]
28680         };
28681
28682         return  cfg;
28683     },
28684     initEvents : function()
28685     {
28686         //Roo.log('add add pane handler');
28687         this.on('addpane', this.onAddPane, this);
28688     },
28689      /**
28690      * Updates the box title
28691      * @param {String} html to set the title to.
28692      */
28693     setTitle : function(value)
28694     {
28695         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28696     },
28697     onAddPane : function(pane)
28698     {
28699         this.panes.push(pane);
28700         //Roo.log('addpane');
28701         //Roo.log(pane);
28702         // tabs are rendere left to right..
28703         if(!this.showtabs){
28704             return;
28705         }
28706         
28707         var ctr = this.el.select('.nav-tabs', true).first();
28708          
28709          
28710         var existing = ctr.select('.nav-tab',true);
28711         var qty = existing.getCount();;
28712         
28713         
28714         var tab = ctr.createChild({
28715             tag : 'li',
28716             cls : 'nav-tab' + (qty ? '' : ' active'),
28717             cn : [
28718                 {
28719                     tag : 'a',
28720                     href:'#',
28721                     html : pane.title
28722                 }
28723             ]
28724         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28725         pane.tab = tab;
28726         
28727         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28728         if (!qty) {
28729             pane.el.addClass('active');
28730         }
28731         
28732                 
28733     },
28734     onTabClick : function(ev,un,ob,pane)
28735     {
28736         //Roo.log('tab - prev default');
28737         ev.preventDefault();
28738         
28739         
28740         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28741         pane.tab.addClass('active');
28742         //Roo.log(pane.title);
28743         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28744         // technically we should have a deactivate event.. but maybe add later.
28745         // and it should not de-activate the selected tab...
28746         this.fireEvent('activatepane', pane);
28747         pane.el.addClass('active');
28748         pane.fireEvent('activate');
28749         
28750         
28751     },
28752     
28753     getActivePane : function()
28754     {
28755         var r = false;
28756         Roo.each(this.panes, function(p) {
28757             if(p.el.hasClass('active')){
28758                 r = p;
28759                 return false;
28760             }
28761             
28762             return;
28763         });
28764         
28765         return r;
28766     }
28767     
28768     
28769 });
28770
28771  
28772 /*
28773  * - LGPL
28774  *
28775  * Tab pane
28776  * 
28777  */
28778 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28779 /**
28780  * @class Roo.bootstrap.TabPane
28781  * @extends Roo.bootstrap.Component
28782  * Bootstrap TabPane class
28783  * @cfg {Boolean} active (false | true) Default false
28784  * @cfg {String} title title of panel
28785
28786  * 
28787  * @constructor
28788  * Create a new TabPane
28789  * @param {Object} config The config object
28790  */
28791
28792 Roo.bootstrap.dash.TabPane = function(config){
28793     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28794     
28795     this.addEvents({
28796         // raw events
28797         /**
28798          * @event activate
28799          * When a pane is activated
28800          * @param {Roo.bootstrap.dash.TabPane} pane
28801          */
28802         "activate" : true
28803          
28804     });
28805 };
28806
28807 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28808     
28809     active : false,
28810     title : '',
28811     
28812     // the tabBox that this is attached to.
28813     tab : false,
28814      
28815     getAutoCreate : function() 
28816     {
28817         var cfg = {
28818             tag: 'div',
28819             cls: 'tab-pane'
28820         };
28821         
28822         if(this.active){
28823             cfg.cls += ' active';
28824         }
28825         
28826         return cfg;
28827     },
28828     initEvents  : function()
28829     {
28830         //Roo.log('trigger add pane handler');
28831         this.parent().fireEvent('addpane', this)
28832     },
28833     
28834      /**
28835      * Updates the tab title 
28836      * @param {String} html to set the title to.
28837      */
28838     setTitle: function(str)
28839     {
28840         if (!this.tab) {
28841             return;
28842         }
28843         this.title = str;
28844         this.tab.select('a', true).first().dom.innerHTML = str;
28845         
28846     }
28847     
28848     
28849     
28850 });
28851
28852  
28853
28854
28855  /*
28856  * - LGPL
28857  *
28858  * menu
28859  * 
28860  */
28861 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28862
28863 /**
28864  * @class Roo.bootstrap.menu.Menu
28865  * @extends Roo.bootstrap.Component
28866  * Bootstrap Menu class - container for Menu
28867  * @cfg {String} html Text of the menu
28868  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28869  * @cfg {String} icon Font awesome icon
28870  * @cfg {String} pos Menu align to (top | bottom) default bottom
28871  * 
28872  * 
28873  * @constructor
28874  * Create a new Menu
28875  * @param {Object} config The config object
28876  */
28877
28878
28879 Roo.bootstrap.menu.Menu = function(config){
28880     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28881     
28882     this.addEvents({
28883         /**
28884          * @event beforeshow
28885          * Fires before this menu is displayed
28886          * @param {Roo.bootstrap.menu.Menu} this
28887          */
28888         beforeshow : true,
28889         /**
28890          * @event beforehide
28891          * Fires before this menu is hidden
28892          * @param {Roo.bootstrap.menu.Menu} this
28893          */
28894         beforehide : true,
28895         /**
28896          * @event show
28897          * Fires after this menu is displayed
28898          * @param {Roo.bootstrap.menu.Menu} this
28899          */
28900         show : true,
28901         /**
28902          * @event hide
28903          * Fires after this menu is hidden
28904          * @param {Roo.bootstrap.menu.Menu} this
28905          */
28906         hide : true,
28907         /**
28908          * @event click
28909          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28910          * @param {Roo.bootstrap.menu.Menu} this
28911          * @param {Roo.EventObject} e
28912          */
28913         click : true
28914     });
28915     
28916 };
28917
28918 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28919     
28920     submenu : false,
28921     html : '',
28922     weight : 'default',
28923     icon : false,
28924     pos : 'bottom',
28925     
28926     
28927     getChildContainer : function() {
28928         if(this.isSubMenu){
28929             return this.el;
28930         }
28931         
28932         return this.el.select('ul.dropdown-menu', true).first();  
28933     },
28934     
28935     getAutoCreate : function()
28936     {
28937         var text = [
28938             {
28939                 tag : 'span',
28940                 cls : 'roo-menu-text',
28941                 html : this.html
28942             }
28943         ];
28944         
28945         if(this.icon){
28946             text.unshift({
28947                 tag : 'i',
28948                 cls : 'fa ' + this.icon
28949             })
28950         }
28951         
28952         
28953         var cfg = {
28954             tag : 'div',
28955             cls : 'btn-group',
28956             cn : [
28957                 {
28958                     tag : 'button',
28959                     cls : 'dropdown-button btn btn-' + this.weight,
28960                     cn : text
28961                 },
28962                 {
28963                     tag : 'button',
28964                     cls : 'dropdown-toggle btn btn-' + this.weight,
28965                     cn : [
28966                         {
28967                             tag : 'span',
28968                             cls : 'caret'
28969                         }
28970                     ]
28971                 },
28972                 {
28973                     tag : 'ul',
28974                     cls : 'dropdown-menu'
28975                 }
28976             ]
28977             
28978         };
28979         
28980         if(this.pos == 'top'){
28981             cfg.cls += ' dropup';
28982         }
28983         
28984         if(this.isSubMenu){
28985             cfg = {
28986                 tag : 'ul',
28987                 cls : 'dropdown-menu'
28988             }
28989         }
28990         
28991         return cfg;
28992     },
28993     
28994     onRender : function(ct, position)
28995     {
28996         this.isSubMenu = ct.hasClass('dropdown-submenu');
28997         
28998         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28999     },
29000     
29001     initEvents : function() 
29002     {
29003         if(this.isSubMenu){
29004             return;
29005         }
29006         
29007         this.hidden = true;
29008         
29009         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29010         this.triggerEl.on('click', this.onTriggerPress, this);
29011         
29012         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29013         this.buttonEl.on('click', this.onClick, this);
29014         
29015     },
29016     
29017     list : function()
29018     {
29019         if(this.isSubMenu){
29020             return this.el;
29021         }
29022         
29023         return this.el.select('ul.dropdown-menu', true).first();
29024     },
29025     
29026     onClick : function(e)
29027     {
29028         this.fireEvent("click", this, e);
29029     },
29030     
29031     onTriggerPress  : function(e)
29032     {   
29033         if (this.isVisible()) {
29034             this.hide();
29035         } else {
29036             this.show();
29037         }
29038     },
29039     
29040     isVisible : function(){
29041         return !this.hidden;
29042     },
29043     
29044     show : function()
29045     {
29046         this.fireEvent("beforeshow", this);
29047         
29048         this.hidden = false;
29049         this.el.addClass('open');
29050         
29051         Roo.get(document).on("mouseup", this.onMouseUp, this);
29052         
29053         this.fireEvent("show", this);
29054         
29055         
29056     },
29057     
29058     hide : function()
29059     {
29060         this.fireEvent("beforehide", this);
29061         
29062         this.hidden = true;
29063         this.el.removeClass('open');
29064         
29065         Roo.get(document).un("mouseup", this.onMouseUp);
29066         
29067         this.fireEvent("hide", this);
29068     },
29069     
29070     onMouseUp : function()
29071     {
29072         this.hide();
29073     }
29074     
29075 });
29076
29077  
29078  /*
29079  * - LGPL
29080  *
29081  * menu item
29082  * 
29083  */
29084 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29085
29086 /**
29087  * @class Roo.bootstrap.menu.Item
29088  * @extends Roo.bootstrap.Component
29089  * Bootstrap MenuItem class
29090  * @cfg {Boolean} submenu (true | false) default false
29091  * @cfg {String} html text of the item
29092  * @cfg {String} href the link
29093  * @cfg {Boolean} disable (true | false) default false
29094  * @cfg {Boolean} preventDefault (true | false) default true
29095  * @cfg {String} icon Font awesome icon
29096  * @cfg {String} pos Submenu align to (left | right) default right 
29097  * 
29098  * 
29099  * @constructor
29100  * Create a new Item
29101  * @param {Object} config The config object
29102  */
29103
29104
29105 Roo.bootstrap.menu.Item = function(config){
29106     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29107     this.addEvents({
29108         /**
29109          * @event mouseover
29110          * Fires when the mouse is hovering over this menu
29111          * @param {Roo.bootstrap.menu.Item} this
29112          * @param {Roo.EventObject} e
29113          */
29114         mouseover : true,
29115         /**
29116          * @event mouseout
29117          * Fires when the mouse exits this menu
29118          * @param {Roo.bootstrap.menu.Item} this
29119          * @param {Roo.EventObject} e
29120          */
29121         mouseout : true,
29122         // raw events
29123         /**
29124          * @event click
29125          * The raw click event for the entire grid.
29126          * @param {Roo.EventObject} e
29127          */
29128         click : true
29129     });
29130 };
29131
29132 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29133     
29134     submenu : false,
29135     href : '',
29136     html : '',
29137     preventDefault: true,
29138     disable : false,
29139     icon : false,
29140     pos : 'right',
29141     
29142     getAutoCreate : function()
29143     {
29144         var text = [
29145             {
29146                 tag : 'span',
29147                 cls : 'roo-menu-item-text',
29148                 html : this.html
29149             }
29150         ];
29151         
29152         if(this.icon){
29153             text.unshift({
29154                 tag : 'i',
29155                 cls : 'fa ' + this.icon
29156             })
29157         }
29158         
29159         var cfg = {
29160             tag : 'li',
29161             cn : [
29162                 {
29163                     tag : 'a',
29164                     href : this.href || '#',
29165                     cn : text
29166                 }
29167             ]
29168         };
29169         
29170         if(this.disable){
29171             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29172         }
29173         
29174         if(this.submenu){
29175             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29176             
29177             if(this.pos == 'left'){
29178                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29179             }
29180         }
29181         
29182         return cfg;
29183     },
29184     
29185     initEvents : function() 
29186     {
29187         this.el.on('mouseover', this.onMouseOver, this);
29188         this.el.on('mouseout', this.onMouseOut, this);
29189         
29190         this.el.select('a', true).first().on('click', this.onClick, this);
29191         
29192     },
29193     
29194     onClick : function(e)
29195     {
29196         if(this.preventDefault){
29197             e.preventDefault();
29198         }
29199         
29200         this.fireEvent("click", this, e);
29201     },
29202     
29203     onMouseOver : function(e)
29204     {
29205         if(this.submenu && this.pos == 'left'){
29206             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29207         }
29208         
29209         this.fireEvent("mouseover", this, e);
29210     },
29211     
29212     onMouseOut : function(e)
29213     {
29214         this.fireEvent("mouseout", this, e);
29215     }
29216 });
29217
29218  
29219
29220  /*
29221  * - LGPL
29222  *
29223  * menu separator
29224  * 
29225  */
29226 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29227
29228 /**
29229  * @class Roo.bootstrap.menu.Separator
29230  * @extends Roo.bootstrap.Component
29231  * Bootstrap Separator class
29232  * 
29233  * @constructor
29234  * Create a new Separator
29235  * @param {Object} config The config object
29236  */
29237
29238
29239 Roo.bootstrap.menu.Separator = function(config){
29240     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29241 };
29242
29243 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29244     
29245     getAutoCreate : function(){
29246         var cfg = {
29247             tag : 'li',
29248             cls: 'dropdown-divider divider'
29249         };
29250         
29251         return cfg;
29252     }
29253    
29254 });
29255
29256  
29257
29258  /*
29259  * - LGPL
29260  *
29261  * Tooltip
29262  * 
29263  */
29264
29265 /**
29266  * @class Roo.bootstrap.Tooltip
29267  * Bootstrap Tooltip class
29268  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29269  * to determine which dom element triggers the tooltip.
29270  * 
29271  * It needs to add support for additional attributes like tooltip-position
29272  * 
29273  * @constructor
29274  * Create a new Toolti
29275  * @param {Object} config The config object
29276  */
29277
29278 Roo.bootstrap.Tooltip = function(config){
29279     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29280     
29281     this.alignment = Roo.bootstrap.Tooltip.alignment;
29282     
29283     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29284         this.alignment = config.alignment;
29285     }
29286     
29287 };
29288
29289 Roo.apply(Roo.bootstrap.Tooltip, {
29290     /**
29291      * @function init initialize tooltip monitoring.
29292      * @static
29293      */
29294     currentEl : false,
29295     currentTip : false,
29296     currentRegion : false,
29297     
29298     //  init : delay?
29299     
29300     init : function()
29301     {
29302         Roo.get(document).on('mouseover', this.enter ,this);
29303         Roo.get(document).on('mouseout', this.leave, this);
29304          
29305         
29306         this.currentTip = new Roo.bootstrap.Tooltip();
29307     },
29308     
29309     enter : function(ev)
29310     {
29311         var dom = ev.getTarget();
29312         
29313         //Roo.log(['enter',dom]);
29314         var el = Roo.fly(dom);
29315         if (this.currentEl) {
29316             //Roo.log(dom);
29317             //Roo.log(this.currentEl);
29318             //Roo.log(this.currentEl.contains(dom));
29319             if (this.currentEl == el) {
29320                 return;
29321             }
29322             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29323                 return;
29324             }
29325
29326         }
29327         
29328         if (this.currentTip.el) {
29329             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29330         }    
29331         //Roo.log(ev);
29332         
29333         if(!el || el.dom == document){
29334             return;
29335         }
29336         
29337         var bindEl = el; 
29338         var pel = false;
29339         if (!el.attr('tooltip')) {
29340             pel = el.findParent("[tooltip]");
29341             if (pel) {
29342                 bindEl = Roo.get(pel);
29343             }
29344         }
29345         
29346        
29347         
29348         // you can not look for children, as if el is the body.. then everythign is the child..
29349         if (!pel && !el.attr('tooltip')) { //
29350             if (!el.select("[tooltip]").elements.length) {
29351                 return;
29352             }
29353             // is the mouse over this child...?
29354             bindEl = el.select("[tooltip]").first();
29355             var xy = ev.getXY();
29356             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29357                 //Roo.log("not in region.");
29358                 return;
29359             }
29360             //Roo.log("child element over..");
29361             
29362         }
29363         this.currentEl = el;
29364         this.currentTip.bind(bindEl);
29365         this.currentRegion = Roo.lib.Region.getRegion(dom);
29366         this.currentTip.enter();
29367         
29368     },
29369     leave : function(ev)
29370     {
29371         var dom = ev.getTarget();
29372         //Roo.log(['leave',dom]);
29373         if (!this.currentEl) {
29374             return;
29375         }
29376         
29377         
29378         if (dom != this.currentEl.dom) {
29379             return;
29380         }
29381         var xy = ev.getXY();
29382         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29383             return;
29384         }
29385         // only activate leave if mouse cursor is outside... bounding box..
29386         
29387         
29388         
29389         
29390         if (this.currentTip) {
29391             this.currentTip.leave();
29392         }
29393         //Roo.log('clear currentEl');
29394         this.currentEl = false;
29395         
29396         
29397     },
29398     alignment : {
29399         'left' : ['r-l', [-2,0], 'right'],
29400         'right' : ['l-r', [2,0], 'left'],
29401         'bottom' : ['t-b', [0,2], 'top'],
29402         'top' : [ 'b-t', [0,-2], 'bottom']
29403     }
29404     
29405 });
29406
29407
29408 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29409     
29410     
29411     bindEl : false,
29412     
29413     delay : null, // can be { show : 300 , hide: 500}
29414     
29415     timeout : null,
29416     
29417     hoverState : null, //???
29418     
29419     placement : 'bottom', 
29420     
29421     alignment : false,
29422     
29423     getAutoCreate : function(){
29424     
29425         var cfg = {
29426            cls : 'tooltip',   
29427            role : 'tooltip',
29428            cn : [
29429                 {
29430                     cls : 'tooltip-arrow arrow'
29431                 },
29432                 {
29433                     cls : 'tooltip-inner'
29434                 }
29435            ]
29436         };
29437         
29438         return cfg;
29439     },
29440     bind : function(el)
29441     {
29442         this.bindEl = el;
29443     },
29444     
29445     initEvents : function()
29446     {
29447         this.arrowEl = this.el.select('.arrow', true).first();
29448         this.innerEl = this.el.select('.tooltip-inner', true).first();
29449     },
29450     
29451     enter : function () {
29452        
29453         if (this.timeout != null) {
29454             clearTimeout(this.timeout);
29455         }
29456         
29457         this.hoverState = 'in';
29458          //Roo.log("enter - show");
29459         if (!this.delay || !this.delay.show) {
29460             this.show();
29461             return;
29462         }
29463         var _t = this;
29464         this.timeout = setTimeout(function () {
29465             if (_t.hoverState == 'in') {
29466                 _t.show();
29467             }
29468         }, this.delay.show);
29469     },
29470     leave : function()
29471     {
29472         clearTimeout(this.timeout);
29473     
29474         this.hoverState = 'out';
29475          if (!this.delay || !this.delay.hide) {
29476             this.hide();
29477             return;
29478         }
29479        
29480         var _t = this;
29481         this.timeout = setTimeout(function () {
29482             //Roo.log("leave - timeout");
29483             
29484             if (_t.hoverState == 'out') {
29485                 _t.hide();
29486                 Roo.bootstrap.Tooltip.currentEl = false;
29487             }
29488         }, delay);
29489     },
29490     
29491     show : function (msg)
29492     {
29493         if (!this.el) {
29494             this.render(document.body);
29495         }
29496         // set content.
29497         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29498         
29499         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29500         
29501         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29502         
29503         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29504                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29505         
29506         var placement = typeof this.placement == 'function' ?
29507             this.placement.call(this, this.el, on_el) :
29508             this.placement;
29509             
29510         var autoToken = /\s?auto?\s?/i;
29511         var autoPlace = autoToken.test(placement);
29512         if (autoPlace) {
29513             placement = placement.replace(autoToken, '') || 'top';
29514         }
29515         
29516         //this.el.detach()
29517         //this.el.setXY([0,0]);
29518         this.el.show();
29519         //this.el.dom.style.display='block';
29520         
29521         //this.el.appendTo(on_el);
29522         
29523         var p = this.getPosition();
29524         var box = this.el.getBox();
29525         
29526         if (autoPlace) {
29527             // fixme..
29528         }
29529         
29530         var align = this.alignment[placement];
29531         
29532         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29533         
29534         if(placement == 'top' || placement == 'bottom'){
29535             if(xy[0] < 0){
29536                 placement = 'right';
29537             }
29538             
29539             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29540                 placement = 'left';
29541             }
29542             
29543             var scroll = Roo.select('body', true).first().getScroll();
29544             
29545             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29546                 placement = 'top';
29547             }
29548             
29549             align = this.alignment[placement];
29550             
29551             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29552             
29553         }
29554         
29555         var elems = document.getElementsByTagName('div');
29556         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29557         for (var i = 0; i < elems.length; i++) {
29558           var zindex = Number.parseInt(
29559                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29560                 10
29561           );
29562           if (zindex > highest) {
29563             highest = zindex;
29564           }
29565         }
29566         
29567         
29568         
29569         this.el.dom.style.zIndex = highest;
29570         
29571         this.el.alignTo(this.bindEl, align[0],align[1]);
29572         //var arrow = this.el.select('.arrow',true).first();
29573         //arrow.set(align[2], 
29574         
29575         this.el.addClass(placement);
29576         this.el.addClass("bs-tooltip-"+ placement);
29577         
29578         this.el.addClass('in fade show');
29579         
29580         this.hoverState = null;
29581         
29582         if (this.el.hasClass('fade')) {
29583             // fade it?
29584         }
29585         
29586         
29587         
29588         
29589         
29590     },
29591     hide : function()
29592     {
29593          
29594         if (!this.el) {
29595             return;
29596         }
29597         //this.el.setXY([0,0]);
29598         this.el.removeClass(['show', 'in']);
29599         //this.el.hide();
29600         
29601     }
29602     
29603 });
29604  
29605
29606  /*
29607  * - LGPL
29608  *
29609  * Location Picker
29610  * 
29611  */
29612
29613 /**
29614  * @class Roo.bootstrap.LocationPicker
29615  * @extends Roo.bootstrap.Component
29616  * Bootstrap LocationPicker class
29617  * @cfg {Number} latitude Position when init default 0
29618  * @cfg {Number} longitude Position when init default 0
29619  * @cfg {Number} zoom default 15
29620  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29621  * @cfg {Boolean} mapTypeControl default false
29622  * @cfg {Boolean} disableDoubleClickZoom default false
29623  * @cfg {Boolean} scrollwheel default true
29624  * @cfg {Boolean} streetViewControl default false
29625  * @cfg {Number} radius default 0
29626  * @cfg {String} locationName
29627  * @cfg {Boolean} draggable default true
29628  * @cfg {Boolean} enableAutocomplete default false
29629  * @cfg {Boolean} enableReverseGeocode default true
29630  * @cfg {String} markerTitle
29631  * 
29632  * @constructor
29633  * Create a new LocationPicker
29634  * @param {Object} config The config object
29635  */
29636
29637
29638 Roo.bootstrap.LocationPicker = function(config){
29639     
29640     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29641     
29642     this.addEvents({
29643         /**
29644          * @event initial
29645          * Fires when the picker initialized.
29646          * @param {Roo.bootstrap.LocationPicker} this
29647          * @param {Google Location} location
29648          */
29649         initial : true,
29650         /**
29651          * @event positionchanged
29652          * Fires when the picker position changed.
29653          * @param {Roo.bootstrap.LocationPicker} this
29654          * @param {Google Location} location
29655          */
29656         positionchanged : true,
29657         /**
29658          * @event resize
29659          * Fires when the map resize.
29660          * @param {Roo.bootstrap.LocationPicker} this
29661          */
29662         resize : true,
29663         /**
29664          * @event show
29665          * Fires when the map show.
29666          * @param {Roo.bootstrap.LocationPicker} this
29667          */
29668         show : true,
29669         /**
29670          * @event hide
29671          * Fires when the map hide.
29672          * @param {Roo.bootstrap.LocationPicker} this
29673          */
29674         hide : true,
29675         /**
29676          * @event mapClick
29677          * Fires when click the map.
29678          * @param {Roo.bootstrap.LocationPicker} this
29679          * @param {Map event} e
29680          */
29681         mapClick : true,
29682         /**
29683          * @event mapRightClick
29684          * Fires when right click the map.
29685          * @param {Roo.bootstrap.LocationPicker} this
29686          * @param {Map event} e
29687          */
29688         mapRightClick : true,
29689         /**
29690          * @event markerClick
29691          * Fires when click the marker.
29692          * @param {Roo.bootstrap.LocationPicker} this
29693          * @param {Map event} e
29694          */
29695         markerClick : true,
29696         /**
29697          * @event markerRightClick
29698          * Fires when right click the marker.
29699          * @param {Roo.bootstrap.LocationPicker} this
29700          * @param {Map event} e
29701          */
29702         markerRightClick : true,
29703         /**
29704          * @event OverlayViewDraw
29705          * Fires when OverlayView Draw
29706          * @param {Roo.bootstrap.LocationPicker} this
29707          */
29708         OverlayViewDraw : true,
29709         /**
29710          * @event OverlayViewOnAdd
29711          * Fires when OverlayView Draw
29712          * @param {Roo.bootstrap.LocationPicker} this
29713          */
29714         OverlayViewOnAdd : true,
29715         /**
29716          * @event OverlayViewOnRemove
29717          * Fires when OverlayView Draw
29718          * @param {Roo.bootstrap.LocationPicker} this
29719          */
29720         OverlayViewOnRemove : true,
29721         /**
29722          * @event OverlayViewShow
29723          * Fires when OverlayView Draw
29724          * @param {Roo.bootstrap.LocationPicker} this
29725          * @param {Pixel} cpx
29726          */
29727         OverlayViewShow : true,
29728         /**
29729          * @event OverlayViewHide
29730          * Fires when OverlayView Draw
29731          * @param {Roo.bootstrap.LocationPicker} this
29732          */
29733         OverlayViewHide : true,
29734         /**
29735          * @event loadexception
29736          * Fires when load google lib failed.
29737          * @param {Roo.bootstrap.LocationPicker} this
29738          */
29739         loadexception : true
29740     });
29741         
29742 };
29743
29744 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29745     
29746     gMapContext: false,
29747     
29748     latitude: 0,
29749     longitude: 0,
29750     zoom: 15,
29751     mapTypeId: false,
29752     mapTypeControl: false,
29753     disableDoubleClickZoom: false,
29754     scrollwheel: true,
29755     streetViewControl: false,
29756     radius: 0,
29757     locationName: '',
29758     draggable: true,
29759     enableAutocomplete: false,
29760     enableReverseGeocode: true,
29761     markerTitle: '',
29762     
29763     getAutoCreate: function()
29764     {
29765
29766         var cfg = {
29767             tag: 'div',
29768             cls: 'roo-location-picker'
29769         };
29770         
29771         return cfg
29772     },
29773     
29774     initEvents: function(ct, position)
29775     {       
29776         if(!this.el.getWidth() || this.isApplied()){
29777             return;
29778         }
29779         
29780         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29781         
29782         this.initial();
29783     },
29784     
29785     initial: function()
29786     {
29787         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29788             this.fireEvent('loadexception', this);
29789             return;
29790         }
29791         
29792         if(!this.mapTypeId){
29793             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29794         }
29795         
29796         this.gMapContext = this.GMapContext();
29797         
29798         this.initOverlayView();
29799         
29800         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29801         
29802         var _this = this;
29803                 
29804         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29805             _this.setPosition(_this.gMapContext.marker.position);
29806         });
29807         
29808         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29809             _this.fireEvent('mapClick', this, event);
29810             
29811         });
29812
29813         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29814             _this.fireEvent('mapRightClick', this, event);
29815             
29816         });
29817         
29818         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29819             _this.fireEvent('markerClick', this, event);
29820             
29821         });
29822
29823         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29824             _this.fireEvent('markerRightClick', this, event);
29825             
29826         });
29827         
29828         this.setPosition(this.gMapContext.location);
29829         
29830         this.fireEvent('initial', this, this.gMapContext.location);
29831     },
29832     
29833     initOverlayView: function()
29834     {
29835         var _this = this;
29836         
29837         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29838             
29839             draw: function()
29840             {
29841                 _this.fireEvent('OverlayViewDraw', _this);
29842             },
29843             
29844             onAdd: function()
29845             {
29846                 _this.fireEvent('OverlayViewOnAdd', _this);
29847             },
29848             
29849             onRemove: function()
29850             {
29851                 _this.fireEvent('OverlayViewOnRemove', _this);
29852             },
29853             
29854             show: function(cpx)
29855             {
29856                 _this.fireEvent('OverlayViewShow', _this, cpx);
29857             },
29858             
29859             hide: function()
29860             {
29861                 _this.fireEvent('OverlayViewHide', _this);
29862             }
29863             
29864         });
29865     },
29866     
29867     fromLatLngToContainerPixel: function(event)
29868     {
29869         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29870     },
29871     
29872     isApplied: function() 
29873     {
29874         return this.getGmapContext() == false ? false : true;
29875     },
29876     
29877     getGmapContext: function() 
29878     {
29879         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29880     },
29881     
29882     GMapContext: function() 
29883     {
29884         var position = new google.maps.LatLng(this.latitude, this.longitude);
29885         
29886         var _map = new google.maps.Map(this.el.dom, {
29887             center: position,
29888             zoom: this.zoom,
29889             mapTypeId: this.mapTypeId,
29890             mapTypeControl: this.mapTypeControl,
29891             disableDoubleClickZoom: this.disableDoubleClickZoom,
29892             scrollwheel: this.scrollwheel,
29893             streetViewControl: this.streetViewControl,
29894             locationName: this.locationName,
29895             draggable: this.draggable,
29896             enableAutocomplete: this.enableAutocomplete,
29897             enableReverseGeocode: this.enableReverseGeocode
29898         });
29899         
29900         var _marker = new google.maps.Marker({
29901             position: position,
29902             map: _map,
29903             title: this.markerTitle,
29904             draggable: this.draggable
29905         });
29906         
29907         return {
29908             map: _map,
29909             marker: _marker,
29910             circle: null,
29911             location: position,
29912             radius: this.radius,
29913             locationName: this.locationName,
29914             addressComponents: {
29915                 formatted_address: null,
29916                 addressLine1: null,
29917                 addressLine2: null,
29918                 streetName: null,
29919                 streetNumber: null,
29920                 city: null,
29921                 district: null,
29922                 state: null,
29923                 stateOrProvince: null
29924             },
29925             settings: this,
29926             domContainer: this.el.dom,
29927             geodecoder: new google.maps.Geocoder()
29928         };
29929     },
29930     
29931     drawCircle: function(center, radius, options) 
29932     {
29933         if (this.gMapContext.circle != null) {
29934             this.gMapContext.circle.setMap(null);
29935         }
29936         if (radius > 0) {
29937             radius *= 1;
29938             options = Roo.apply({}, options, {
29939                 strokeColor: "#0000FF",
29940                 strokeOpacity: .35,
29941                 strokeWeight: 2,
29942                 fillColor: "#0000FF",
29943                 fillOpacity: .2
29944             });
29945             
29946             options.map = this.gMapContext.map;
29947             options.radius = radius;
29948             options.center = center;
29949             this.gMapContext.circle = new google.maps.Circle(options);
29950             return this.gMapContext.circle;
29951         }
29952         
29953         return null;
29954     },
29955     
29956     setPosition: function(location) 
29957     {
29958         this.gMapContext.location = location;
29959         this.gMapContext.marker.setPosition(location);
29960         this.gMapContext.map.panTo(location);
29961         this.drawCircle(location, this.gMapContext.radius, {});
29962         
29963         var _this = this;
29964         
29965         if (this.gMapContext.settings.enableReverseGeocode) {
29966             this.gMapContext.geodecoder.geocode({
29967                 latLng: this.gMapContext.location
29968             }, function(results, status) {
29969                 
29970                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29971                     _this.gMapContext.locationName = results[0].formatted_address;
29972                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29973                     
29974                     _this.fireEvent('positionchanged', this, location);
29975                 }
29976             });
29977             
29978             return;
29979         }
29980         
29981         this.fireEvent('positionchanged', this, location);
29982     },
29983     
29984     resize: function()
29985     {
29986         google.maps.event.trigger(this.gMapContext.map, "resize");
29987         
29988         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29989         
29990         this.fireEvent('resize', this);
29991     },
29992     
29993     setPositionByLatLng: function(latitude, longitude)
29994     {
29995         this.setPosition(new google.maps.LatLng(latitude, longitude));
29996     },
29997     
29998     getCurrentPosition: function() 
29999     {
30000         return {
30001             latitude: this.gMapContext.location.lat(),
30002             longitude: this.gMapContext.location.lng()
30003         };
30004     },
30005     
30006     getAddressName: function() 
30007     {
30008         return this.gMapContext.locationName;
30009     },
30010     
30011     getAddressComponents: function() 
30012     {
30013         return this.gMapContext.addressComponents;
30014     },
30015     
30016     address_component_from_google_geocode: function(address_components) 
30017     {
30018         var result = {};
30019         
30020         for (var i = 0; i < address_components.length; i++) {
30021             var component = address_components[i];
30022             if (component.types.indexOf("postal_code") >= 0) {
30023                 result.postalCode = component.short_name;
30024             } else if (component.types.indexOf("street_number") >= 0) {
30025                 result.streetNumber = component.short_name;
30026             } else if (component.types.indexOf("route") >= 0) {
30027                 result.streetName = component.short_name;
30028             } else if (component.types.indexOf("neighborhood") >= 0) {
30029                 result.city = component.short_name;
30030             } else if (component.types.indexOf("locality") >= 0) {
30031                 result.city = component.short_name;
30032             } else if (component.types.indexOf("sublocality") >= 0) {
30033                 result.district = component.short_name;
30034             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30035                 result.stateOrProvince = component.short_name;
30036             } else if (component.types.indexOf("country") >= 0) {
30037                 result.country = component.short_name;
30038             }
30039         }
30040         
30041         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30042         result.addressLine2 = "";
30043         return result;
30044     },
30045     
30046     setZoomLevel: function(zoom)
30047     {
30048         this.gMapContext.map.setZoom(zoom);
30049     },
30050     
30051     show: function()
30052     {
30053         if(!this.el){
30054             return;
30055         }
30056         
30057         this.el.show();
30058         
30059         this.resize();
30060         
30061         this.fireEvent('show', this);
30062     },
30063     
30064     hide: function()
30065     {
30066         if(!this.el){
30067             return;
30068         }
30069         
30070         this.el.hide();
30071         
30072         this.fireEvent('hide', this);
30073     }
30074     
30075 });
30076
30077 Roo.apply(Roo.bootstrap.LocationPicker, {
30078     
30079     OverlayView : function(map, options)
30080     {
30081         options = options || {};
30082         
30083         this.setMap(map);
30084     }
30085     
30086     
30087 });/**
30088  * @class Roo.bootstrap.Alert
30089  * @extends Roo.bootstrap.Component
30090  * Bootstrap Alert class - shows an alert area box
30091  * eg
30092  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30093   Enter a valid email address
30094 </div>
30095  * @licence LGPL
30096  * @cfg {String} title The title of alert
30097  * @cfg {String} html The content of alert
30098  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30099  * @cfg {String} fa font-awesomeicon
30100  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30101  * @cfg {Boolean} close true to show a x closer
30102  * 
30103  * 
30104  * @constructor
30105  * Create a new alert
30106  * @param {Object} config The config object
30107  */
30108
30109
30110 Roo.bootstrap.Alert = function(config){
30111     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30112     
30113 };
30114
30115 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30116     
30117     title: '',
30118     html: '',
30119     weight: false,
30120     fa: false,
30121     faicon: false, // BC
30122     close : false,
30123     
30124     
30125     getAutoCreate : function()
30126     {
30127         
30128         var cfg = {
30129             tag : 'div',
30130             cls : 'alert',
30131             cn : [
30132                 {
30133                     tag: 'button',
30134                     type :  "button",
30135                     cls: "close",
30136                     html : '×',
30137                     style : this.close ? '' : 'display:none'
30138                 },
30139                 {
30140                     tag : 'i',
30141                     cls : 'roo-alert-icon'
30142                     
30143                 },
30144                 {
30145                     tag : 'b',
30146                     cls : 'roo-alert-title',
30147                     html : this.title
30148                 },
30149                 {
30150                     tag : 'span',
30151                     cls : 'roo-alert-text',
30152                     html : this.html
30153                 }
30154             ]
30155         };
30156         
30157         if(this.faicon){
30158             cfg.cn[0].cls += ' fa ' + this.faicon;
30159         }
30160         if(this.fa){
30161             cfg.cn[0].cls += ' fa ' + this.fa;
30162         }
30163         
30164         if(this.weight){
30165             cfg.cls += ' alert-' + this.weight;
30166         }
30167         
30168         return cfg;
30169     },
30170     
30171     initEvents: function() 
30172     {
30173         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30174         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30175         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30176         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30177         if (this.seconds > 0) {
30178             this.hide.defer(this.seconds, this);
30179         }
30180     },
30181     /**
30182      * Set the Title Message HTML
30183      * @param {String} html
30184      */
30185     setTitle : function(str)
30186     {
30187         this.titleEl.dom.innerHTML = str;
30188     },
30189      
30190      /**
30191      * Set the Body Message HTML
30192      * @param {String} html
30193      */
30194     setHtml : function(str)
30195     {
30196         this.htmlEl.dom.innerHTML = str;
30197     },
30198     /**
30199      * Set the Weight of the alert
30200      * @param {String} (success|info|warning|danger) weight
30201      */
30202     
30203     setWeight : function(weight)
30204     {
30205         if(this.weight){
30206             this.el.removeClass('alert-' + this.weight);
30207         }
30208         
30209         this.weight = weight;
30210         
30211         this.el.addClass('alert-' + this.weight);
30212     },
30213       /**
30214      * Set the Icon of the alert
30215      * @param {String} see fontawsome names (name without the 'fa-' bit)
30216      */
30217     setIcon : function(icon)
30218     {
30219         if(this.faicon){
30220             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30221         }
30222         
30223         this.faicon = icon;
30224         
30225         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30226     },
30227     /**
30228      * Hide the Alert
30229      */
30230     hide: function() 
30231     {
30232         this.el.hide();   
30233     },
30234     /**
30235      * Show the Alert
30236      */
30237     show: function() 
30238     {  
30239         this.el.show();   
30240     }
30241     
30242 });
30243
30244  
30245 /*
30246 * Licence: LGPL
30247 */
30248
30249 /**
30250  * @class Roo.bootstrap.UploadCropbox
30251  * @extends Roo.bootstrap.Component
30252  * Bootstrap UploadCropbox class
30253  * @cfg {String} emptyText show when image has been loaded
30254  * @cfg {String} rotateNotify show when image too small to rotate
30255  * @cfg {Number} errorTimeout default 3000
30256  * @cfg {Number} minWidth default 300
30257  * @cfg {Number} minHeight default 300
30258  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30259  * @cfg {Boolean} isDocument (true|false) default false
30260  * @cfg {String} url action url
30261  * @cfg {String} paramName default 'imageUpload'
30262  * @cfg {String} method default POST
30263  * @cfg {Boolean} loadMask (true|false) default true
30264  * @cfg {Boolean} loadingText default 'Loading...'
30265  * 
30266  * @constructor
30267  * Create a new UploadCropbox
30268  * @param {Object} config The config object
30269  */
30270
30271 Roo.bootstrap.UploadCropbox = function(config){
30272     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30273     
30274     this.addEvents({
30275         /**
30276          * @event beforeselectfile
30277          * Fire before select file
30278          * @param {Roo.bootstrap.UploadCropbox} this
30279          */
30280         "beforeselectfile" : true,
30281         /**
30282          * @event initial
30283          * Fire after initEvent
30284          * @param {Roo.bootstrap.UploadCropbox} this
30285          */
30286         "initial" : true,
30287         /**
30288          * @event crop
30289          * Fire after initEvent
30290          * @param {Roo.bootstrap.UploadCropbox} this
30291          * @param {String} data
30292          */
30293         "crop" : true,
30294         /**
30295          * @event prepare
30296          * Fire when preparing the file data
30297          * @param {Roo.bootstrap.UploadCropbox} this
30298          * @param {Object} file
30299          */
30300         "prepare" : true,
30301         /**
30302          * @event exception
30303          * Fire when get exception
30304          * @param {Roo.bootstrap.UploadCropbox} this
30305          * @param {XMLHttpRequest} xhr
30306          */
30307         "exception" : true,
30308         /**
30309          * @event beforeloadcanvas
30310          * Fire before load the canvas
30311          * @param {Roo.bootstrap.UploadCropbox} this
30312          * @param {String} src
30313          */
30314         "beforeloadcanvas" : true,
30315         /**
30316          * @event trash
30317          * Fire when trash image
30318          * @param {Roo.bootstrap.UploadCropbox} this
30319          */
30320         "trash" : true,
30321         /**
30322          * @event download
30323          * Fire when download the image
30324          * @param {Roo.bootstrap.UploadCropbox} this
30325          */
30326         "download" : true,
30327         /**
30328          * @event footerbuttonclick
30329          * Fire when footerbuttonclick
30330          * @param {Roo.bootstrap.UploadCropbox} this
30331          * @param {String} type
30332          */
30333         "footerbuttonclick" : true,
30334         /**
30335          * @event resize
30336          * Fire when resize
30337          * @param {Roo.bootstrap.UploadCropbox} this
30338          */
30339         "resize" : true,
30340         /**
30341          * @event rotate
30342          * Fire when rotate the image
30343          * @param {Roo.bootstrap.UploadCropbox} this
30344          * @param {String} pos
30345          */
30346         "rotate" : true,
30347         /**
30348          * @event inspect
30349          * Fire when inspect the file
30350          * @param {Roo.bootstrap.UploadCropbox} this
30351          * @param {Object} file
30352          */
30353         "inspect" : true,
30354         /**
30355          * @event upload
30356          * Fire when xhr upload the file
30357          * @param {Roo.bootstrap.UploadCropbox} this
30358          * @param {Object} data
30359          */
30360         "upload" : true,
30361         /**
30362          * @event arrange
30363          * Fire when arrange the file data
30364          * @param {Roo.bootstrap.UploadCropbox} this
30365          * @param {Object} formData
30366          */
30367         "arrange" : true
30368     });
30369     
30370     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30371 };
30372
30373 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30374     
30375     emptyText : 'Click to upload image',
30376     rotateNotify : 'Image is too small to rotate',
30377     errorTimeout : 3000,
30378     scale : 0,
30379     baseScale : 1,
30380     rotate : 0,
30381     dragable : false,
30382     pinching : false,
30383     mouseX : 0,
30384     mouseY : 0,
30385     cropData : false,
30386     minWidth : 300,
30387     minHeight : 300,
30388     file : false,
30389     exif : {},
30390     baseRotate : 1,
30391     cropType : 'image/jpeg',
30392     buttons : false,
30393     canvasLoaded : false,
30394     isDocument : false,
30395     method : 'POST',
30396     paramName : 'imageUpload',
30397     loadMask : true,
30398     loadingText : 'Loading...',
30399     maskEl : false,
30400     
30401     getAutoCreate : function()
30402     {
30403         var cfg = {
30404             tag : 'div',
30405             cls : 'roo-upload-cropbox',
30406             cn : [
30407                 {
30408                     tag : 'input',
30409                     cls : 'roo-upload-cropbox-selector',
30410                     type : 'file'
30411                 },
30412                 {
30413                     tag : 'div',
30414                     cls : 'roo-upload-cropbox-body',
30415                     style : 'cursor:pointer',
30416                     cn : [
30417                         {
30418                             tag : 'div',
30419                             cls : 'roo-upload-cropbox-preview'
30420                         },
30421                         {
30422                             tag : 'div',
30423                             cls : 'roo-upload-cropbox-thumb'
30424                         },
30425                         {
30426                             tag : 'div',
30427                             cls : 'roo-upload-cropbox-empty-notify',
30428                             html : this.emptyText
30429                         },
30430                         {
30431                             tag : 'div',
30432                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30433                             html : this.rotateNotify
30434                         }
30435                     ]
30436                 },
30437                 {
30438                     tag : 'div',
30439                     cls : 'roo-upload-cropbox-footer',
30440                     cn : {
30441                         tag : 'div',
30442                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30443                         cn : []
30444                     }
30445                 }
30446             ]
30447         };
30448         
30449         return cfg;
30450     },
30451     
30452     onRender : function(ct, position)
30453     {
30454         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30455         
30456         if (this.buttons.length) {
30457             
30458             Roo.each(this.buttons, function(bb) {
30459                 
30460                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30461                 
30462                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30463                 
30464             }, this);
30465         }
30466         
30467         if(this.loadMask){
30468             this.maskEl = this.el;
30469         }
30470     },
30471     
30472     initEvents : function()
30473     {
30474         this.urlAPI = (window.createObjectURL && window) || 
30475                                 (window.URL && URL.revokeObjectURL && URL) || 
30476                                 (window.webkitURL && webkitURL);
30477                         
30478         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30479         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30480         
30481         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30482         this.selectorEl.hide();
30483         
30484         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30485         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30486         
30487         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30488         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30489         this.thumbEl.hide();
30490         
30491         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30492         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30493         
30494         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30495         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30496         this.errorEl.hide();
30497         
30498         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30499         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30500         this.footerEl.hide();
30501         
30502         this.setThumbBoxSize();
30503         
30504         this.bind();
30505         
30506         this.resize();
30507         
30508         this.fireEvent('initial', this);
30509     },
30510
30511     bind : function()
30512     {
30513         var _this = this;
30514         
30515         window.addEventListener("resize", function() { _this.resize(); } );
30516         
30517         this.bodyEl.on('click', this.beforeSelectFile, this);
30518         
30519         if(Roo.isTouch){
30520             this.bodyEl.on('touchstart', this.onTouchStart, this);
30521             this.bodyEl.on('touchmove', this.onTouchMove, this);
30522             this.bodyEl.on('touchend', this.onTouchEnd, this);
30523         }
30524         
30525         if(!Roo.isTouch){
30526             this.bodyEl.on('mousedown', this.onMouseDown, this);
30527             this.bodyEl.on('mousemove', this.onMouseMove, this);
30528             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30529             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30530             Roo.get(document).on('mouseup', this.onMouseUp, this);
30531         }
30532         
30533         this.selectorEl.on('change', this.onFileSelected, this);
30534     },
30535     
30536     reset : function()
30537     {    
30538         this.scale = 0;
30539         this.baseScale = 1;
30540         this.rotate = 0;
30541         this.baseRotate = 1;
30542         this.dragable = false;
30543         this.pinching = false;
30544         this.mouseX = 0;
30545         this.mouseY = 0;
30546         this.cropData = false;
30547         this.notifyEl.dom.innerHTML = this.emptyText;
30548         
30549         this.selectorEl.dom.value = '';
30550         
30551     },
30552     
30553     resize : function()
30554     {
30555         if(this.fireEvent('resize', this) != false){
30556             this.setThumbBoxPosition();
30557             this.setCanvasPosition();
30558         }
30559     },
30560     
30561     onFooterButtonClick : function(e, el, o, type)
30562     {
30563         switch (type) {
30564             case 'rotate-left' :
30565                 this.onRotateLeft(e);
30566                 break;
30567             case 'rotate-right' :
30568                 this.onRotateRight(e);
30569                 break;
30570             case 'picture' :
30571                 this.beforeSelectFile(e);
30572                 break;
30573             case 'trash' :
30574                 this.trash(e);
30575                 break;
30576             case 'crop' :
30577                 this.crop(e);
30578                 break;
30579             case 'download' :
30580                 this.download(e);
30581                 break;
30582             default :
30583                 break;
30584         }
30585         
30586         this.fireEvent('footerbuttonclick', this, type);
30587     },
30588     
30589     beforeSelectFile : function(e)
30590     {
30591         e.preventDefault();
30592         
30593         if(this.fireEvent('beforeselectfile', this) != false){
30594             this.selectorEl.dom.click();
30595         }
30596     },
30597     
30598     onFileSelected : function(e)
30599     {
30600         e.preventDefault();
30601         
30602         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30603             return;
30604         }
30605         
30606         var file = this.selectorEl.dom.files[0];
30607         
30608         if(this.fireEvent('inspect', this, file) != false){
30609             this.prepare(file);
30610         }
30611         
30612     },
30613     
30614     trash : function(e)
30615     {
30616         this.fireEvent('trash', this);
30617     },
30618     
30619     download : function(e)
30620     {
30621         this.fireEvent('download', this);
30622     },
30623     
30624     loadCanvas : function(src)
30625     {   
30626         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30627             
30628             this.reset();
30629             
30630             this.imageEl = document.createElement('img');
30631             
30632             var _this = this;
30633             
30634             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30635             
30636             this.imageEl.src = src;
30637         }
30638     },
30639     
30640     onLoadCanvas : function()
30641     {   
30642         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30643         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30644         
30645         this.bodyEl.un('click', this.beforeSelectFile, this);
30646         
30647         this.notifyEl.hide();
30648         this.thumbEl.show();
30649         this.footerEl.show();
30650         
30651         this.baseRotateLevel();
30652         
30653         if(this.isDocument){
30654             this.setThumbBoxSize();
30655         }
30656         
30657         this.setThumbBoxPosition();
30658         
30659         this.baseScaleLevel();
30660         
30661         this.draw();
30662         
30663         this.resize();
30664         
30665         this.canvasLoaded = true;
30666         
30667         if(this.loadMask){
30668             this.maskEl.unmask();
30669         }
30670         
30671     },
30672     
30673     setCanvasPosition : function()
30674     {   
30675         if(!this.canvasEl){
30676             return;
30677         }
30678         
30679         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30680         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30681         
30682         this.previewEl.setLeft(pw);
30683         this.previewEl.setTop(ph);
30684         
30685     },
30686     
30687     onMouseDown : function(e)
30688     {   
30689         e.stopEvent();
30690         
30691         this.dragable = true;
30692         this.pinching = false;
30693         
30694         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30695             this.dragable = false;
30696             return;
30697         }
30698         
30699         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30700         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30701         
30702     },
30703     
30704     onMouseMove : function(e)
30705     {   
30706         e.stopEvent();
30707         
30708         if(!this.canvasLoaded){
30709             return;
30710         }
30711         
30712         if (!this.dragable){
30713             return;
30714         }
30715         
30716         var minX = Math.ceil(this.thumbEl.getLeft(true));
30717         var minY = Math.ceil(this.thumbEl.getTop(true));
30718         
30719         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30720         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30721         
30722         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30723         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30724         
30725         x = x - this.mouseX;
30726         y = y - this.mouseY;
30727         
30728         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30729         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30730         
30731         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30732         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30733         
30734         this.previewEl.setLeft(bgX);
30735         this.previewEl.setTop(bgY);
30736         
30737         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30738         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30739     },
30740     
30741     onMouseUp : function(e)
30742     {   
30743         e.stopEvent();
30744         
30745         this.dragable = false;
30746     },
30747     
30748     onMouseWheel : function(e)
30749     {   
30750         e.stopEvent();
30751         
30752         this.startScale = this.scale;
30753         
30754         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30755         
30756         if(!this.zoomable()){
30757             this.scale = this.startScale;
30758             return;
30759         }
30760         
30761         this.draw();
30762         
30763         return;
30764     },
30765     
30766     zoomable : function()
30767     {
30768         var minScale = this.thumbEl.getWidth() / this.minWidth;
30769         
30770         if(this.minWidth < this.minHeight){
30771             minScale = this.thumbEl.getHeight() / this.minHeight;
30772         }
30773         
30774         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30775         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30776         
30777         if(
30778                 this.isDocument &&
30779                 (this.rotate == 0 || this.rotate == 180) && 
30780                 (
30781                     width > this.imageEl.OriginWidth || 
30782                     height > this.imageEl.OriginHeight ||
30783                     (width < this.minWidth && height < this.minHeight)
30784                 )
30785         ){
30786             return false;
30787         }
30788         
30789         if(
30790                 this.isDocument &&
30791                 (this.rotate == 90 || this.rotate == 270) && 
30792                 (
30793                     width > this.imageEl.OriginWidth || 
30794                     height > this.imageEl.OriginHeight ||
30795                     (width < this.minHeight && height < this.minWidth)
30796                 )
30797         ){
30798             return false;
30799         }
30800         
30801         if(
30802                 !this.isDocument &&
30803                 (this.rotate == 0 || this.rotate == 180) && 
30804                 (
30805                     width < this.minWidth || 
30806                     width > this.imageEl.OriginWidth || 
30807                     height < this.minHeight || 
30808                     height > this.imageEl.OriginHeight
30809                 )
30810         ){
30811             return false;
30812         }
30813         
30814         if(
30815                 !this.isDocument &&
30816                 (this.rotate == 90 || this.rotate == 270) && 
30817                 (
30818                     width < this.minHeight || 
30819                     width > this.imageEl.OriginWidth || 
30820                     height < this.minWidth || 
30821                     height > this.imageEl.OriginHeight
30822                 )
30823         ){
30824             return false;
30825         }
30826         
30827         return true;
30828         
30829     },
30830     
30831     onRotateLeft : function(e)
30832     {   
30833         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30834             
30835             var minScale = this.thumbEl.getWidth() / this.minWidth;
30836             
30837             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30838             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30839             
30840             this.startScale = this.scale;
30841             
30842             while (this.getScaleLevel() < minScale){
30843             
30844                 this.scale = this.scale + 1;
30845                 
30846                 if(!this.zoomable()){
30847                     break;
30848                 }
30849                 
30850                 if(
30851                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30852                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30853                 ){
30854                     continue;
30855                 }
30856                 
30857                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30858
30859                 this.draw();
30860                 
30861                 return;
30862             }
30863             
30864             this.scale = this.startScale;
30865             
30866             this.onRotateFail();
30867             
30868             return false;
30869         }
30870         
30871         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30872
30873         if(this.isDocument){
30874             this.setThumbBoxSize();
30875             this.setThumbBoxPosition();
30876             this.setCanvasPosition();
30877         }
30878         
30879         this.draw();
30880         
30881         this.fireEvent('rotate', this, 'left');
30882         
30883     },
30884     
30885     onRotateRight : function(e)
30886     {
30887         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30888             
30889             var minScale = this.thumbEl.getWidth() / this.minWidth;
30890         
30891             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30892             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30893             
30894             this.startScale = this.scale;
30895             
30896             while (this.getScaleLevel() < minScale){
30897             
30898                 this.scale = this.scale + 1;
30899                 
30900                 if(!this.zoomable()){
30901                     break;
30902                 }
30903                 
30904                 if(
30905                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30906                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30907                 ){
30908                     continue;
30909                 }
30910                 
30911                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30912
30913                 this.draw();
30914                 
30915                 return;
30916             }
30917             
30918             this.scale = this.startScale;
30919             
30920             this.onRotateFail();
30921             
30922             return false;
30923         }
30924         
30925         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30926
30927         if(this.isDocument){
30928             this.setThumbBoxSize();
30929             this.setThumbBoxPosition();
30930             this.setCanvasPosition();
30931         }
30932         
30933         this.draw();
30934         
30935         this.fireEvent('rotate', this, 'right');
30936     },
30937     
30938     onRotateFail : function()
30939     {
30940         this.errorEl.show(true);
30941         
30942         var _this = this;
30943         
30944         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30945     },
30946     
30947     draw : function()
30948     {
30949         this.previewEl.dom.innerHTML = '';
30950         
30951         var canvasEl = document.createElement("canvas");
30952         
30953         var contextEl = canvasEl.getContext("2d");
30954         
30955         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30956         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30957         var center = this.imageEl.OriginWidth / 2;
30958         
30959         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30960             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30961             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30962             center = this.imageEl.OriginHeight / 2;
30963         }
30964         
30965         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30966         
30967         contextEl.translate(center, center);
30968         contextEl.rotate(this.rotate * Math.PI / 180);
30969
30970         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30971         
30972         this.canvasEl = document.createElement("canvas");
30973         
30974         this.contextEl = this.canvasEl.getContext("2d");
30975         
30976         switch (this.rotate) {
30977             case 0 :
30978                 
30979                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30980                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30981                 
30982                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30983                 
30984                 break;
30985             case 90 : 
30986                 
30987                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30988                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30989                 
30990                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30991                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30992                     break;
30993                 }
30994                 
30995                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30996                 
30997                 break;
30998             case 180 :
30999                 
31000                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31001                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31002                 
31003                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31004                     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);
31005                     break;
31006                 }
31007                 
31008                 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);
31009                 
31010                 break;
31011             case 270 :
31012                 
31013                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31014                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31015         
31016                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31017                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31018                     break;
31019                 }
31020                 
31021                 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);
31022                 
31023                 break;
31024             default : 
31025                 break;
31026         }
31027         
31028         this.previewEl.appendChild(this.canvasEl);
31029         
31030         this.setCanvasPosition();
31031     },
31032     
31033     crop : function()
31034     {
31035         if(!this.canvasLoaded){
31036             return;
31037         }
31038         
31039         var imageCanvas = document.createElement("canvas");
31040         
31041         var imageContext = imageCanvas.getContext("2d");
31042         
31043         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31044         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31045         
31046         var center = imageCanvas.width / 2;
31047         
31048         imageContext.translate(center, center);
31049         
31050         imageContext.rotate(this.rotate * Math.PI / 180);
31051         
31052         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31053         
31054         var canvas = document.createElement("canvas");
31055         
31056         var context = canvas.getContext("2d");
31057                 
31058         canvas.width = this.minWidth;
31059         canvas.height = this.minHeight;
31060
31061         switch (this.rotate) {
31062             case 0 :
31063                 
31064                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31065                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31066                 
31067                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31068                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31069                 
31070                 var targetWidth = this.minWidth - 2 * x;
31071                 var targetHeight = this.minHeight - 2 * y;
31072                 
31073                 var scale = 1;
31074                 
31075                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31076                     scale = targetWidth / width;
31077                 }
31078                 
31079                 if(x > 0 && y == 0){
31080                     scale = targetHeight / height;
31081                 }
31082                 
31083                 if(x > 0 && y > 0){
31084                     scale = targetWidth / width;
31085                     
31086                     if(width < height){
31087                         scale = targetHeight / height;
31088                     }
31089                 }
31090                 
31091                 context.scale(scale, scale);
31092                 
31093                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31094                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31095
31096                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31097                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31098
31099                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31100                 
31101                 break;
31102             case 90 : 
31103                 
31104                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31105                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31106                 
31107                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31108                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31109                 
31110                 var targetWidth = this.minWidth - 2 * x;
31111                 var targetHeight = this.minHeight - 2 * y;
31112                 
31113                 var scale = 1;
31114                 
31115                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31116                     scale = targetWidth / width;
31117                 }
31118                 
31119                 if(x > 0 && y == 0){
31120                     scale = targetHeight / height;
31121                 }
31122                 
31123                 if(x > 0 && y > 0){
31124                     scale = targetWidth / width;
31125                     
31126                     if(width < height){
31127                         scale = targetHeight / height;
31128                     }
31129                 }
31130                 
31131                 context.scale(scale, scale);
31132                 
31133                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31134                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31135
31136                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31137                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31138                 
31139                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31140                 
31141                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31142                 
31143                 break;
31144             case 180 :
31145                 
31146                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31147                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31148                 
31149                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31150                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31151                 
31152                 var targetWidth = this.minWidth - 2 * x;
31153                 var targetHeight = this.minHeight - 2 * y;
31154                 
31155                 var scale = 1;
31156                 
31157                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31158                     scale = targetWidth / width;
31159                 }
31160                 
31161                 if(x > 0 && y == 0){
31162                     scale = targetHeight / height;
31163                 }
31164                 
31165                 if(x > 0 && y > 0){
31166                     scale = targetWidth / width;
31167                     
31168                     if(width < height){
31169                         scale = targetHeight / height;
31170                     }
31171                 }
31172                 
31173                 context.scale(scale, scale);
31174                 
31175                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31176                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31177
31178                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31179                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31180
31181                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31182                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31183                 
31184                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31185                 
31186                 break;
31187             case 270 :
31188                 
31189                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31190                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31191                 
31192                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31193                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31194                 
31195                 var targetWidth = this.minWidth - 2 * x;
31196                 var targetHeight = this.minHeight - 2 * y;
31197                 
31198                 var scale = 1;
31199                 
31200                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31201                     scale = targetWidth / width;
31202                 }
31203                 
31204                 if(x > 0 && y == 0){
31205                     scale = targetHeight / height;
31206                 }
31207                 
31208                 if(x > 0 && y > 0){
31209                     scale = targetWidth / width;
31210                     
31211                     if(width < height){
31212                         scale = targetHeight / height;
31213                     }
31214                 }
31215                 
31216                 context.scale(scale, scale);
31217                 
31218                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31219                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31220
31221                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31222                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31223                 
31224                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31225                 
31226                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31227                 
31228                 break;
31229             default : 
31230                 break;
31231         }
31232         
31233         this.cropData = canvas.toDataURL(this.cropType);
31234         
31235         if(this.fireEvent('crop', this, this.cropData) !== false){
31236             this.process(this.file, this.cropData);
31237         }
31238         
31239         return;
31240         
31241     },
31242     
31243     setThumbBoxSize : function()
31244     {
31245         var width, height;
31246         
31247         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31248             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31249             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31250             
31251             this.minWidth = width;
31252             this.minHeight = height;
31253             
31254             if(this.rotate == 90 || this.rotate == 270){
31255                 this.minWidth = height;
31256                 this.minHeight = width;
31257             }
31258         }
31259         
31260         height = 300;
31261         width = Math.ceil(this.minWidth * height / this.minHeight);
31262         
31263         if(this.minWidth > this.minHeight){
31264             width = 300;
31265             height = Math.ceil(this.minHeight * width / this.minWidth);
31266         }
31267         
31268         this.thumbEl.setStyle({
31269             width : width + 'px',
31270             height : height + 'px'
31271         });
31272
31273         return;
31274             
31275     },
31276     
31277     setThumbBoxPosition : function()
31278     {
31279         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31280         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31281         
31282         this.thumbEl.setLeft(x);
31283         this.thumbEl.setTop(y);
31284         
31285     },
31286     
31287     baseRotateLevel : function()
31288     {
31289         this.baseRotate = 1;
31290         
31291         if(
31292                 typeof(this.exif) != 'undefined' &&
31293                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31294                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31295         ){
31296             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31297         }
31298         
31299         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31300         
31301     },
31302     
31303     baseScaleLevel : function()
31304     {
31305         var width, height;
31306         
31307         if(this.isDocument){
31308             
31309             if(this.baseRotate == 6 || this.baseRotate == 8){
31310             
31311                 height = this.thumbEl.getHeight();
31312                 this.baseScale = height / this.imageEl.OriginWidth;
31313
31314                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31315                     width = this.thumbEl.getWidth();
31316                     this.baseScale = width / this.imageEl.OriginHeight;
31317                 }
31318
31319                 return;
31320             }
31321
31322             height = this.thumbEl.getHeight();
31323             this.baseScale = height / this.imageEl.OriginHeight;
31324
31325             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31326                 width = this.thumbEl.getWidth();
31327                 this.baseScale = width / this.imageEl.OriginWidth;
31328             }
31329
31330             return;
31331         }
31332         
31333         if(this.baseRotate == 6 || this.baseRotate == 8){
31334             
31335             width = this.thumbEl.getHeight();
31336             this.baseScale = width / this.imageEl.OriginHeight;
31337             
31338             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31339                 height = this.thumbEl.getWidth();
31340                 this.baseScale = height / this.imageEl.OriginHeight;
31341             }
31342             
31343             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31344                 height = this.thumbEl.getWidth();
31345                 this.baseScale = height / this.imageEl.OriginHeight;
31346                 
31347                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31348                     width = this.thumbEl.getHeight();
31349                     this.baseScale = width / this.imageEl.OriginWidth;
31350                 }
31351             }
31352             
31353             return;
31354         }
31355         
31356         width = this.thumbEl.getWidth();
31357         this.baseScale = width / this.imageEl.OriginWidth;
31358         
31359         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31360             height = this.thumbEl.getHeight();
31361             this.baseScale = height / this.imageEl.OriginHeight;
31362         }
31363         
31364         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31365             
31366             height = this.thumbEl.getHeight();
31367             this.baseScale = height / this.imageEl.OriginHeight;
31368             
31369             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31370                 width = this.thumbEl.getWidth();
31371                 this.baseScale = width / this.imageEl.OriginWidth;
31372             }
31373             
31374         }
31375         
31376         return;
31377     },
31378     
31379     getScaleLevel : function()
31380     {
31381         return this.baseScale * Math.pow(1.1, this.scale);
31382     },
31383     
31384     onTouchStart : function(e)
31385     {
31386         if(!this.canvasLoaded){
31387             this.beforeSelectFile(e);
31388             return;
31389         }
31390         
31391         var touches = e.browserEvent.touches;
31392         
31393         if(!touches){
31394             return;
31395         }
31396         
31397         if(touches.length == 1){
31398             this.onMouseDown(e);
31399             return;
31400         }
31401         
31402         if(touches.length != 2){
31403             return;
31404         }
31405         
31406         var coords = [];
31407         
31408         for(var i = 0, finger; finger = touches[i]; i++){
31409             coords.push(finger.pageX, finger.pageY);
31410         }
31411         
31412         var x = Math.pow(coords[0] - coords[2], 2);
31413         var y = Math.pow(coords[1] - coords[3], 2);
31414         
31415         this.startDistance = Math.sqrt(x + y);
31416         
31417         this.startScale = this.scale;
31418         
31419         this.pinching = true;
31420         this.dragable = false;
31421         
31422     },
31423     
31424     onTouchMove : function(e)
31425     {
31426         if(!this.pinching && !this.dragable){
31427             return;
31428         }
31429         
31430         var touches = e.browserEvent.touches;
31431         
31432         if(!touches){
31433             return;
31434         }
31435         
31436         if(this.dragable){
31437             this.onMouseMove(e);
31438             return;
31439         }
31440         
31441         var coords = [];
31442         
31443         for(var i = 0, finger; finger = touches[i]; i++){
31444             coords.push(finger.pageX, finger.pageY);
31445         }
31446         
31447         var x = Math.pow(coords[0] - coords[2], 2);
31448         var y = Math.pow(coords[1] - coords[3], 2);
31449         
31450         this.endDistance = Math.sqrt(x + y);
31451         
31452         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31453         
31454         if(!this.zoomable()){
31455             this.scale = this.startScale;
31456             return;
31457         }
31458         
31459         this.draw();
31460         
31461     },
31462     
31463     onTouchEnd : function(e)
31464     {
31465         this.pinching = false;
31466         this.dragable = false;
31467         
31468     },
31469     
31470     process : function(file, crop)
31471     {
31472         if(this.loadMask){
31473             this.maskEl.mask(this.loadingText);
31474         }
31475         
31476         this.xhr = new XMLHttpRequest();
31477         
31478         file.xhr = this.xhr;
31479
31480         this.xhr.open(this.method, this.url, true);
31481         
31482         var headers = {
31483             "Accept": "application/json",
31484             "Cache-Control": "no-cache",
31485             "X-Requested-With": "XMLHttpRequest"
31486         };
31487         
31488         for (var headerName in headers) {
31489             var headerValue = headers[headerName];
31490             if (headerValue) {
31491                 this.xhr.setRequestHeader(headerName, headerValue);
31492             }
31493         }
31494         
31495         var _this = this;
31496         
31497         this.xhr.onload = function()
31498         {
31499             _this.xhrOnLoad(_this.xhr);
31500         }
31501         
31502         this.xhr.onerror = function()
31503         {
31504             _this.xhrOnError(_this.xhr);
31505         }
31506         
31507         var formData = new FormData();
31508
31509         formData.append('returnHTML', 'NO');
31510         
31511         if(crop){
31512             formData.append('crop', crop);
31513         }
31514         
31515         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31516             formData.append(this.paramName, file, file.name);
31517         }
31518         
31519         if(typeof(file.filename) != 'undefined'){
31520             formData.append('filename', file.filename);
31521         }
31522         
31523         if(typeof(file.mimetype) != 'undefined'){
31524             formData.append('mimetype', file.mimetype);
31525         }
31526         
31527         if(this.fireEvent('arrange', this, formData) != false){
31528             this.xhr.send(formData);
31529         };
31530     },
31531     
31532     xhrOnLoad : function(xhr)
31533     {
31534         if(this.loadMask){
31535             this.maskEl.unmask();
31536         }
31537         
31538         if (xhr.readyState !== 4) {
31539             this.fireEvent('exception', this, xhr);
31540             return;
31541         }
31542
31543         var response = Roo.decode(xhr.responseText);
31544         
31545         if(!response.success){
31546             this.fireEvent('exception', this, xhr);
31547             return;
31548         }
31549         
31550         var response = Roo.decode(xhr.responseText);
31551         
31552         this.fireEvent('upload', this, response);
31553         
31554     },
31555     
31556     xhrOnError : function()
31557     {
31558         if(this.loadMask){
31559             this.maskEl.unmask();
31560         }
31561         
31562         Roo.log('xhr on error');
31563         
31564         var response = Roo.decode(xhr.responseText);
31565           
31566         Roo.log(response);
31567         
31568     },
31569     
31570     prepare : function(file)
31571     {   
31572         if(this.loadMask){
31573             this.maskEl.mask(this.loadingText);
31574         }
31575         
31576         this.file = false;
31577         this.exif = {};
31578         
31579         if(typeof(file) === 'string'){
31580             this.loadCanvas(file);
31581             return;
31582         }
31583         
31584         if(!file || !this.urlAPI){
31585             return;
31586         }
31587         
31588         this.file = file;
31589         this.cropType = file.type;
31590         
31591         var _this = this;
31592         
31593         if(this.fireEvent('prepare', this, this.file) != false){
31594             
31595             var reader = new FileReader();
31596             
31597             reader.onload = function (e) {
31598                 if (e.target.error) {
31599                     Roo.log(e.target.error);
31600                     return;
31601                 }
31602                 
31603                 var buffer = e.target.result,
31604                     dataView = new DataView(buffer),
31605                     offset = 2,
31606                     maxOffset = dataView.byteLength - 4,
31607                     markerBytes,
31608                     markerLength;
31609                 
31610                 if (dataView.getUint16(0) === 0xffd8) {
31611                     while (offset < maxOffset) {
31612                         markerBytes = dataView.getUint16(offset);
31613                         
31614                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31615                             markerLength = dataView.getUint16(offset + 2) + 2;
31616                             if (offset + markerLength > dataView.byteLength) {
31617                                 Roo.log('Invalid meta data: Invalid segment size.');
31618                                 break;
31619                             }
31620                             
31621                             if(markerBytes == 0xffe1){
31622                                 _this.parseExifData(
31623                                     dataView,
31624                                     offset,
31625                                     markerLength
31626                                 );
31627                             }
31628                             
31629                             offset += markerLength;
31630                             
31631                             continue;
31632                         }
31633                         
31634                         break;
31635                     }
31636                     
31637                 }
31638                 
31639                 var url = _this.urlAPI.createObjectURL(_this.file);
31640                 
31641                 _this.loadCanvas(url);
31642                 
31643                 return;
31644             }
31645             
31646             reader.readAsArrayBuffer(this.file);
31647             
31648         }
31649         
31650     },
31651     
31652     parseExifData : function(dataView, offset, length)
31653     {
31654         var tiffOffset = offset + 10,
31655             littleEndian,
31656             dirOffset;
31657     
31658         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31659             // No Exif data, might be XMP data instead
31660             return;
31661         }
31662         
31663         // Check for the ASCII code for "Exif" (0x45786966):
31664         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31665             // No Exif data, might be XMP data instead
31666             return;
31667         }
31668         if (tiffOffset + 8 > dataView.byteLength) {
31669             Roo.log('Invalid Exif data: Invalid segment size.');
31670             return;
31671         }
31672         // Check for the two null bytes:
31673         if (dataView.getUint16(offset + 8) !== 0x0000) {
31674             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31675             return;
31676         }
31677         // Check the byte alignment:
31678         switch (dataView.getUint16(tiffOffset)) {
31679         case 0x4949:
31680             littleEndian = true;
31681             break;
31682         case 0x4D4D:
31683             littleEndian = false;
31684             break;
31685         default:
31686             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31687             return;
31688         }
31689         // Check for the TIFF tag marker (0x002A):
31690         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31691             Roo.log('Invalid Exif data: Missing TIFF marker.');
31692             return;
31693         }
31694         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31695         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31696         
31697         this.parseExifTags(
31698             dataView,
31699             tiffOffset,
31700             tiffOffset + dirOffset,
31701             littleEndian
31702         );
31703     },
31704     
31705     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31706     {
31707         var tagsNumber,
31708             dirEndOffset,
31709             i;
31710         if (dirOffset + 6 > dataView.byteLength) {
31711             Roo.log('Invalid Exif data: Invalid directory offset.');
31712             return;
31713         }
31714         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31715         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31716         if (dirEndOffset + 4 > dataView.byteLength) {
31717             Roo.log('Invalid Exif data: Invalid directory size.');
31718             return;
31719         }
31720         for (i = 0; i < tagsNumber; i += 1) {
31721             this.parseExifTag(
31722                 dataView,
31723                 tiffOffset,
31724                 dirOffset + 2 + 12 * i, // tag offset
31725                 littleEndian
31726             );
31727         }
31728         // Return the offset to the next directory:
31729         return dataView.getUint32(dirEndOffset, littleEndian);
31730     },
31731     
31732     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31733     {
31734         var tag = dataView.getUint16(offset, littleEndian);
31735         
31736         this.exif[tag] = this.getExifValue(
31737             dataView,
31738             tiffOffset,
31739             offset,
31740             dataView.getUint16(offset + 2, littleEndian), // tag type
31741             dataView.getUint32(offset + 4, littleEndian), // tag length
31742             littleEndian
31743         );
31744     },
31745     
31746     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31747     {
31748         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31749             tagSize,
31750             dataOffset,
31751             values,
31752             i,
31753             str,
31754             c;
31755     
31756         if (!tagType) {
31757             Roo.log('Invalid Exif data: Invalid tag type.');
31758             return;
31759         }
31760         
31761         tagSize = tagType.size * length;
31762         // Determine if the value is contained in the dataOffset bytes,
31763         // or if the value at the dataOffset is a pointer to the actual data:
31764         dataOffset = tagSize > 4 ?
31765                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31766         if (dataOffset + tagSize > dataView.byteLength) {
31767             Roo.log('Invalid Exif data: Invalid data offset.');
31768             return;
31769         }
31770         if (length === 1) {
31771             return tagType.getValue(dataView, dataOffset, littleEndian);
31772         }
31773         values = [];
31774         for (i = 0; i < length; i += 1) {
31775             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31776         }
31777         
31778         if (tagType.ascii) {
31779             str = '';
31780             // Concatenate the chars:
31781             for (i = 0; i < values.length; i += 1) {
31782                 c = values[i];
31783                 // Ignore the terminating NULL byte(s):
31784                 if (c === '\u0000') {
31785                     break;
31786                 }
31787                 str += c;
31788             }
31789             return str;
31790         }
31791         return values;
31792     }
31793     
31794 });
31795
31796 Roo.apply(Roo.bootstrap.UploadCropbox, {
31797     tags : {
31798         'Orientation': 0x0112
31799     },
31800     
31801     Orientation: {
31802             1: 0, //'top-left',
31803 //            2: 'top-right',
31804             3: 180, //'bottom-right',
31805 //            4: 'bottom-left',
31806 //            5: 'left-top',
31807             6: 90, //'right-top',
31808 //            7: 'right-bottom',
31809             8: 270 //'left-bottom'
31810     },
31811     
31812     exifTagTypes : {
31813         // byte, 8-bit unsigned int:
31814         1: {
31815             getValue: function (dataView, dataOffset) {
31816                 return dataView.getUint8(dataOffset);
31817             },
31818             size: 1
31819         },
31820         // ascii, 8-bit byte:
31821         2: {
31822             getValue: function (dataView, dataOffset) {
31823                 return String.fromCharCode(dataView.getUint8(dataOffset));
31824             },
31825             size: 1,
31826             ascii: true
31827         },
31828         // short, 16 bit int:
31829         3: {
31830             getValue: function (dataView, dataOffset, littleEndian) {
31831                 return dataView.getUint16(dataOffset, littleEndian);
31832             },
31833             size: 2
31834         },
31835         // long, 32 bit int:
31836         4: {
31837             getValue: function (dataView, dataOffset, littleEndian) {
31838                 return dataView.getUint32(dataOffset, littleEndian);
31839             },
31840             size: 4
31841         },
31842         // rational = two long values, first is numerator, second is denominator:
31843         5: {
31844             getValue: function (dataView, dataOffset, littleEndian) {
31845                 return dataView.getUint32(dataOffset, littleEndian) /
31846                     dataView.getUint32(dataOffset + 4, littleEndian);
31847             },
31848             size: 8
31849         },
31850         // slong, 32 bit signed int:
31851         9: {
31852             getValue: function (dataView, dataOffset, littleEndian) {
31853                 return dataView.getInt32(dataOffset, littleEndian);
31854             },
31855             size: 4
31856         },
31857         // srational, two slongs, first is numerator, second is denominator:
31858         10: {
31859             getValue: function (dataView, dataOffset, littleEndian) {
31860                 return dataView.getInt32(dataOffset, littleEndian) /
31861                     dataView.getInt32(dataOffset + 4, littleEndian);
31862             },
31863             size: 8
31864         }
31865     },
31866     
31867     footer : {
31868         STANDARD : [
31869             {
31870                 tag : 'div',
31871                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31872                 action : 'rotate-left',
31873                 cn : [
31874                     {
31875                         tag : 'button',
31876                         cls : 'btn btn-default',
31877                         html : '<i class="fa fa-undo"></i>'
31878                     }
31879                 ]
31880             },
31881             {
31882                 tag : 'div',
31883                 cls : 'btn-group roo-upload-cropbox-picture',
31884                 action : 'picture',
31885                 cn : [
31886                     {
31887                         tag : 'button',
31888                         cls : 'btn btn-default',
31889                         html : '<i class="fa fa-picture-o"></i>'
31890                     }
31891                 ]
31892             },
31893             {
31894                 tag : 'div',
31895                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31896                 action : 'rotate-right',
31897                 cn : [
31898                     {
31899                         tag : 'button',
31900                         cls : 'btn btn-default',
31901                         html : '<i class="fa fa-repeat"></i>'
31902                     }
31903                 ]
31904             }
31905         ],
31906         DOCUMENT : [
31907             {
31908                 tag : 'div',
31909                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31910                 action : 'rotate-left',
31911                 cn : [
31912                     {
31913                         tag : 'button',
31914                         cls : 'btn btn-default',
31915                         html : '<i class="fa fa-undo"></i>'
31916                     }
31917                 ]
31918             },
31919             {
31920                 tag : 'div',
31921                 cls : 'btn-group roo-upload-cropbox-download',
31922                 action : 'download',
31923                 cn : [
31924                     {
31925                         tag : 'button',
31926                         cls : 'btn btn-default',
31927                         html : '<i class="fa fa-download"></i>'
31928                     }
31929                 ]
31930             },
31931             {
31932                 tag : 'div',
31933                 cls : 'btn-group roo-upload-cropbox-crop',
31934                 action : 'crop',
31935                 cn : [
31936                     {
31937                         tag : 'button',
31938                         cls : 'btn btn-default',
31939                         html : '<i class="fa fa-crop"></i>'
31940                     }
31941                 ]
31942             },
31943             {
31944                 tag : 'div',
31945                 cls : 'btn-group roo-upload-cropbox-trash',
31946                 action : 'trash',
31947                 cn : [
31948                     {
31949                         tag : 'button',
31950                         cls : 'btn btn-default',
31951                         html : '<i class="fa fa-trash"></i>'
31952                     }
31953                 ]
31954             },
31955             {
31956                 tag : 'div',
31957                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31958                 action : 'rotate-right',
31959                 cn : [
31960                     {
31961                         tag : 'button',
31962                         cls : 'btn btn-default',
31963                         html : '<i class="fa fa-repeat"></i>'
31964                     }
31965                 ]
31966             }
31967         ],
31968         ROTATOR : [
31969             {
31970                 tag : 'div',
31971                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31972                 action : 'rotate-left',
31973                 cn : [
31974                     {
31975                         tag : 'button',
31976                         cls : 'btn btn-default',
31977                         html : '<i class="fa fa-undo"></i>'
31978                     }
31979                 ]
31980             },
31981             {
31982                 tag : 'div',
31983                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31984                 action : 'rotate-right',
31985                 cn : [
31986                     {
31987                         tag : 'button',
31988                         cls : 'btn btn-default',
31989                         html : '<i class="fa fa-repeat"></i>'
31990                     }
31991                 ]
31992             }
31993         ]
31994     }
31995 });
31996
31997 /*
31998 * Licence: LGPL
31999 */
32000
32001 /**
32002  * @class Roo.bootstrap.DocumentManager
32003  * @extends Roo.bootstrap.Component
32004  * Bootstrap DocumentManager class
32005  * @cfg {String} paramName default 'imageUpload'
32006  * @cfg {String} toolTipName default 'filename'
32007  * @cfg {String} method default POST
32008  * @cfg {String} url action url
32009  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32010  * @cfg {Boolean} multiple multiple upload default true
32011  * @cfg {Number} thumbSize default 300
32012  * @cfg {String} fieldLabel
32013  * @cfg {Number} labelWidth default 4
32014  * @cfg {String} labelAlign (left|top) default left
32015  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32016 * @cfg {Number} labellg set the width of label (1-12)
32017  * @cfg {Number} labelmd set the width of label (1-12)
32018  * @cfg {Number} labelsm set the width of label (1-12)
32019  * @cfg {Number} labelxs set the width of label (1-12)
32020  * 
32021  * @constructor
32022  * Create a new DocumentManager
32023  * @param {Object} config The config object
32024  */
32025
32026 Roo.bootstrap.DocumentManager = function(config){
32027     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32028     
32029     this.files = [];
32030     this.delegates = [];
32031     
32032     this.addEvents({
32033         /**
32034          * @event initial
32035          * Fire when initial the DocumentManager
32036          * @param {Roo.bootstrap.DocumentManager} this
32037          */
32038         "initial" : true,
32039         /**
32040          * @event inspect
32041          * inspect selected file
32042          * @param {Roo.bootstrap.DocumentManager} this
32043          * @param {File} file
32044          */
32045         "inspect" : true,
32046         /**
32047          * @event exception
32048          * Fire when xhr load exception
32049          * @param {Roo.bootstrap.DocumentManager} this
32050          * @param {XMLHttpRequest} xhr
32051          */
32052         "exception" : true,
32053         /**
32054          * @event afterupload
32055          * Fire when xhr load exception
32056          * @param {Roo.bootstrap.DocumentManager} this
32057          * @param {XMLHttpRequest} xhr
32058          */
32059         "afterupload" : true,
32060         /**
32061          * @event prepare
32062          * prepare the form data
32063          * @param {Roo.bootstrap.DocumentManager} this
32064          * @param {Object} formData
32065          */
32066         "prepare" : true,
32067         /**
32068          * @event remove
32069          * Fire when remove the file
32070          * @param {Roo.bootstrap.DocumentManager} this
32071          * @param {Object} file
32072          */
32073         "remove" : true,
32074         /**
32075          * @event refresh
32076          * Fire after refresh the file
32077          * @param {Roo.bootstrap.DocumentManager} this
32078          */
32079         "refresh" : true,
32080         /**
32081          * @event click
32082          * Fire after click the image
32083          * @param {Roo.bootstrap.DocumentManager} this
32084          * @param {Object} file
32085          */
32086         "click" : true,
32087         /**
32088          * @event edit
32089          * Fire when upload a image and editable set to true
32090          * @param {Roo.bootstrap.DocumentManager} this
32091          * @param {Object} file
32092          */
32093         "edit" : true,
32094         /**
32095          * @event beforeselectfile
32096          * Fire before select file
32097          * @param {Roo.bootstrap.DocumentManager} this
32098          */
32099         "beforeselectfile" : true,
32100         /**
32101          * @event process
32102          * Fire before process file
32103          * @param {Roo.bootstrap.DocumentManager} this
32104          * @param {Object} file
32105          */
32106         "process" : true,
32107         /**
32108          * @event previewrendered
32109          * Fire when preview rendered
32110          * @param {Roo.bootstrap.DocumentManager} this
32111          * @param {Object} file
32112          */
32113         "previewrendered" : true,
32114         /**
32115          */
32116         "previewResize" : true
32117         
32118     });
32119 };
32120
32121 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32122     
32123     boxes : 0,
32124     inputName : '',
32125     thumbSize : 300,
32126     multiple : true,
32127     files : false,
32128     method : 'POST',
32129     url : '',
32130     paramName : 'imageUpload',
32131     toolTipName : 'filename',
32132     fieldLabel : '',
32133     labelWidth : 4,
32134     labelAlign : 'left',
32135     editable : true,
32136     delegates : false,
32137     xhr : false, 
32138     
32139     labellg : 0,
32140     labelmd : 0,
32141     labelsm : 0,
32142     labelxs : 0,
32143     
32144     getAutoCreate : function()
32145     {   
32146         var managerWidget = {
32147             tag : 'div',
32148             cls : 'roo-document-manager',
32149             cn : [
32150                 {
32151                     tag : 'input',
32152                     cls : 'roo-document-manager-selector',
32153                     type : 'file'
32154                 },
32155                 {
32156                     tag : 'div',
32157                     cls : 'roo-document-manager-uploader',
32158                     cn : [
32159                         {
32160                             tag : 'div',
32161                             cls : 'roo-document-manager-upload-btn',
32162                             html : '<i class="fa fa-plus"></i>'
32163                         }
32164                     ]
32165                     
32166                 }
32167             ]
32168         };
32169         
32170         var content = [
32171             {
32172                 tag : 'div',
32173                 cls : 'column col-md-12',
32174                 cn : managerWidget
32175             }
32176         ];
32177         
32178         if(this.fieldLabel.length){
32179             
32180             content = [
32181                 {
32182                     tag : 'div',
32183                     cls : 'column col-md-12',
32184                     html : this.fieldLabel
32185                 },
32186                 {
32187                     tag : 'div',
32188                     cls : 'column col-md-12',
32189                     cn : managerWidget
32190                 }
32191             ];
32192
32193             if(this.labelAlign == 'left'){
32194                 content = [
32195                     {
32196                         tag : 'div',
32197                         cls : 'column',
32198                         html : this.fieldLabel
32199                     },
32200                     {
32201                         tag : 'div',
32202                         cls : 'column',
32203                         cn : managerWidget
32204                     }
32205                 ];
32206                 
32207                 if(this.labelWidth > 12){
32208                     content[0].style = "width: " + this.labelWidth + 'px';
32209                 }
32210
32211                 if(this.labelWidth < 13 && this.labelmd == 0){
32212                     this.labelmd = this.labelWidth;
32213                 }
32214
32215                 if(this.labellg > 0){
32216                     content[0].cls += ' col-lg-' + this.labellg;
32217                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32218                 }
32219
32220                 if(this.labelmd > 0){
32221                     content[0].cls += ' col-md-' + this.labelmd;
32222                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32223                 }
32224
32225                 if(this.labelsm > 0){
32226                     content[0].cls += ' col-sm-' + this.labelsm;
32227                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32228                 }
32229
32230                 if(this.labelxs > 0){
32231                     content[0].cls += ' col-xs-' + this.labelxs;
32232                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32233                 }
32234                 
32235             }
32236         }
32237         
32238         var cfg = {
32239             tag : 'div',
32240             cls : 'row clearfix',
32241             cn : content
32242         };
32243         
32244         return cfg;
32245         
32246     },
32247     
32248     initEvents : function()
32249     {
32250         this.managerEl = this.el.select('.roo-document-manager', true).first();
32251         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32252         
32253         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32254         this.selectorEl.hide();
32255         
32256         if(this.multiple){
32257             this.selectorEl.attr('multiple', 'multiple');
32258         }
32259         
32260         this.selectorEl.on('change', this.onFileSelected, this);
32261         
32262         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32263         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32264         
32265         this.uploader.on('click', this.onUploaderClick, this);
32266         
32267         this.renderProgressDialog();
32268         
32269         var _this = this;
32270         
32271         window.addEventListener("resize", function() { _this.refresh(); } );
32272         
32273         this.fireEvent('initial', this);
32274     },
32275     
32276     renderProgressDialog : function()
32277     {
32278         var _this = this;
32279         
32280         this.progressDialog = new Roo.bootstrap.Modal({
32281             cls : 'roo-document-manager-progress-dialog',
32282             allow_close : false,
32283             animate : false,
32284             title : '',
32285             buttons : [
32286                 {
32287                     name  :'cancel',
32288                     weight : 'danger',
32289                     html : 'Cancel'
32290                 }
32291             ], 
32292             listeners : { 
32293                 btnclick : function() {
32294                     _this.uploadCancel();
32295                     this.hide();
32296                 }
32297             }
32298         });
32299          
32300         this.progressDialog.render(Roo.get(document.body));
32301          
32302         this.progress = new Roo.bootstrap.Progress({
32303             cls : 'roo-document-manager-progress',
32304             active : true,
32305             striped : true
32306         });
32307         
32308         this.progress.render(this.progressDialog.getChildContainer());
32309         
32310         this.progressBar = new Roo.bootstrap.ProgressBar({
32311             cls : 'roo-document-manager-progress-bar',
32312             aria_valuenow : 0,
32313             aria_valuemin : 0,
32314             aria_valuemax : 12,
32315             panel : 'success'
32316         });
32317         
32318         this.progressBar.render(this.progress.getChildContainer());
32319     },
32320     
32321     onUploaderClick : function(e)
32322     {
32323         e.preventDefault();
32324      
32325         if(this.fireEvent('beforeselectfile', this) != false){
32326             this.selectorEl.dom.click();
32327         }
32328         
32329     },
32330     
32331     onFileSelected : function(e)
32332     {
32333         e.preventDefault();
32334         
32335         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32336             return;
32337         }
32338         
32339         Roo.each(this.selectorEl.dom.files, function(file){
32340             if(this.fireEvent('inspect', this, file) != false){
32341                 this.files.push(file);
32342             }
32343         }, this);
32344         
32345         this.queue();
32346         
32347     },
32348     
32349     queue : function()
32350     {
32351         this.selectorEl.dom.value = '';
32352         
32353         if(!this.files || !this.files.length){
32354             return;
32355         }
32356         
32357         if(this.boxes > 0 && this.files.length > this.boxes){
32358             this.files = this.files.slice(0, this.boxes);
32359         }
32360         
32361         this.uploader.show();
32362         
32363         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32364             this.uploader.hide();
32365         }
32366         
32367         var _this = this;
32368         
32369         var files = [];
32370         
32371         var docs = [];
32372         
32373         Roo.each(this.files, function(file){
32374             
32375             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32376                 var f = this.renderPreview(file);
32377                 files.push(f);
32378                 return;
32379             }
32380             
32381             if(file.type.indexOf('image') != -1){
32382                 this.delegates.push(
32383                     (function(){
32384                         _this.process(file);
32385                     }).createDelegate(this)
32386                 );
32387         
32388                 return;
32389             }
32390             
32391             docs.push(
32392                 (function(){
32393                     _this.process(file);
32394                 }).createDelegate(this)
32395             );
32396             
32397         }, this);
32398         
32399         this.files = files;
32400         
32401         this.delegates = this.delegates.concat(docs);
32402         
32403         if(!this.delegates.length){
32404             this.refresh();
32405             return;
32406         }
32407         
32408         this.progressBar.aria_valuemax = this.delegates.length;
32409         
32410         this.arrange();
32411         
32412         return;
32413     },
32414     
32415     arrange : function()
32416     {
32417         if(!this.delegates.length){
32418             this.progressDialog.hide();
32419             this.refresh();
32420             return;
32421         }
32422         
32423         var delegate = this.delegates.shift();
32424         
32425         this.progressDialog.show();
32426         
32427         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32428         
32429         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32430         
32431         delegate();
32432     },
32433     
32434     refresh : function()
32435     {
32436         this.uploader.show();
32437         
32438         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32439             this.uploader.hide();
32440         }
32441         
32442         Roo.isTouch ? this.closable(false) : this.closable(true);
32443         
32444         this.fireEvent('refresh', this);
32445     },
32446     
32447     onRemove : function(e, el, o)
32448     {
32449         e.preventDefault();
32450         
32451         this.fireEvent('remove', this, o);
32452         
32453     },
32454     
32455     remove : function(o)
32456     {
32457         var files = [];
32458         
32459         Roo.each(this.files, function(file){
32460             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32461                 files.push(file);
32462                 return;
32463             }
32464
32465             o.target.remove();
32466
32467         }, this);
32468         
32469         this.files = files;
32470         
32471         this.refresh();
32472     },
32473     
32474     clear : function()
32475     {
32476         Roo.each(this.files, function(file){
32477             if(!file.target){
32478                 return;
32479             }
32480             
32481             file.target.remove();
32482
32483         }, this);
32484         
32485         this.files = [];
32486         
32487         this.refresh();
32488     },
32489     
32490     onClick : function(e, el, o)
32491     {
32492         e.preventDefault();
32493         
32494         this.fireEvent('click', this, o);
32495         
32496     },
32497     
32498     closable : function(closable)
32499     {
32500         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32501             
32502             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32503             
32504             if(closable){
32505                 el.show();
32506                 return;
32507             }
32508             
32509             el.hide();
32510             
32511         }, this);
32512     },
32513     
32514     xhrOnLoad : function(xhr)
32515     {
32516         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32517             el.remove();
32518         }, this);
32519         
32520         if (xhr.readyState !== 4) {
32521             this.arrange();
32522             this.fireEvent('exception', this, xhr);
32523             return;
32524         }
32525
32526         var response = Roo.decode(xhr.responseText);
32527         
32528         if(!response.success){
32529             this.arrange();
32530             this.fireEvent('exception', this, xhr);
32531             return;
32532         }
32533         
32534         var file = this.renderPreview(response.data);
32535         
32536         this.files.push(file);
32537         
32538         this.arrange();
32539         
32540         this.fireEvent('afterupload', this, xhr);
32541         
32542     },
32543     
32544     xhrOnError : function(xhr)
32545     {
32546         Roo.log('xhr on error');
32547         
32548         var response = Roo.decode(xhr.responseText);
32549           
32550         Roo.log(response);
32551         
32552         this.arrange();
32553     },
32554     
32555     process : function(file)
32556     {
32557         if(this.fireEvent('process', this, file) !== false){
32558             if(this.editable && file.type.indexOf('image') != -1){
32559                 this.fireEvent('edit', this, file);
32560                 return;
32561             }
32562
32563             this.uploadStart(file, false);
32564
32565             return;
32566         }
32567         
32568     },
32569     
32570     uploadStart : function(file, crop)
32571     {
32572         this.xhr = new XMLHttpRequest();
32573         
32574         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32575             this.arrange();
32576             return;
32577         }
32578         
32579         file.xhr = this.xhr;
32580             
32581         this.managerEl.createChild({
32582             tag : 'div',
32583             cls : 'roo-document-manager-loading',
32584             cn : [
32585                 {
32586                     tag : 'div',
32587                     tooltip : file.name,
32588                     cls : 'roo-document-manager-thumb',
32589                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32590                 }
32591             ]
32592
32593         });
32594
32595         this.xhr.open(this.method, this.url, true);
32596         
32597         var headers = {
32598             "Accept": "application/json",
32599             "Cache-Control": "no-cache",
32600             "X-Requested-With": "XMLHttpRequest"
32601         };
32602         
32603         for (var headerName in headers) {
32604             var headerValue = headers[headerName];
32605             if (headerValue) {
32606                 this.xhr.setRequestHeader(headerName, headerValue);
32607             }
32608         }
32609         
32610         var _this = this;
32611         
32612         this.xhr.onload = function()
32613         {
32614             _this.xhrOnLoad(_this.xhr);
32615         }
32616         
32617         this.xhr.onerror = function()
32618         {
32619             _this.xhrOnError(_this.xhr);
32620         }
32621         
32622         var formData = new FormData();
32623
32624         formData.append('returnHTML', 'NO');
32625         
32626         if(crop){
32627             formData.append('crop', crop);
32628         }
32629         
32630         formData.append(this.paramName, file, file.name);
32631         
32632         var options = {
32633             file : file, 
32634             manually : false
32635         };
32636         
32637         if(this.fireEvent('prepare', this, formData, options) != false){
32638             
32639             if(options.manually){
32640                 return;
32641             }
32642             
32643             this.xhr.send(formData);
32644             return;
32645         };
32646         
32647         this.uploadCancel();
32648     },
32649     
32650     uploadCancel : function()
32651     {
32652         if (this.xhr) {
32653             this.xhr.abort();
32654         }
32655         
32656         this.delegates = [];
32657         
32658         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32659             el.remove();
32660         }, this);
32661         
32662         this.arrange();
32663     },
32664     
32665     renderPreview : function(file)
32666     {
32667         if(typeof(file.target) != 'undefined' && file.target){
32668             return file;
32669         }
32670         
32671         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32672         
32673         var previewEl = this.managerEl.createChild({
32674             tag : 'div',
32675             cls : 'roo-document-manager-preview',
32676             cn : [
32677                 {
32678                     tag : 'div',
32679                     tooltip : file[this.toolTipName],
32680                     cls : 'roo-document-manager-thumb',
32681                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32682                 },
32683                 {
32684                     tag : 'button',
32685                     cls : 'close',
32686                     html : '<i class="fa fa-times-circle"></i>'
32687                 }
32688             ]
32689         });
32690
32691         var close = previewEl.select('button.close', true).first();
32692
32693         close.on('click', this.onRemove, this, file);
32694
32695         file.target = previewEl;
32696
32697         var image = previewEl.select('img', true).first();
32698         
32699         var _this = this;
32700         
32701         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32702         
32703         image.on('click', this.onClick, this, file);
32704         
32705         this.fireEvent('previewrendered', this, file);
32706         
32707         return file;
32708         
32709     },
32710     
32711     onPreviewLoad : function(file, image)
32712     {
32713         if(typeof(file.target) == 'undefined' || !file.target){
32714             return;
32715         }
32716         
32717         var width = image.dom.naturalWidth || image.dom.width;
32718         var height = image.dom.naturalHeight || image.dom.height;
32719         
32720         if(!this.previewResize) {
32721             return;
32722         }
32723         
32724         if(width > height){
32725             file.target.addClass('wide');
32726             return;
32727         }
32728         
32729         file.target.addClass('tall');
32730         return;
32731         
32732     },
32733     
32734     uploadFromSource : function(file, crop)
32735     {
32736         this.xhr = new XMLHttpRequest();
32737         
32738         this.managerEl.createChild({
32739             tag : 'div',
32740             cls : 'roo-document-manager-loading',
32741             cn : [
32742                 {
32743                     tag : 'div',
32744                     tooltip : file.name,
32745                     cls : 'roo-document-manager-thumb',
32746                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32747                 }
32748             ]
32749
32750         });
32751
32752         this.xhr.open(this.method, this.url, true);
32753         
32754         var headers = {
32755             "Accept": "application/json",
32756             "Cache-Control": "no-cache",
32757             "X-Requested-With": "XMLHttpRequest"
32758         };
32759         
32760         for (var headerName in headers) {
32761             var headerValue = headers[headerName];
32762             if (headerValue) {
32763                 this.xhr.setRequestHeader(headerName, headerValue);
32764             }
32765         }
32766         
32767         var _this = this;
32768         
32769         this.xhr.onload = function()
32770         {
32771             _this.xhrOnLoad(_this.xhr);
32772         }
32773         
32774         this.xhr.onerror = function()
32775         {
32776             _this.xhrOnError(_this.xhr);
32777         }
32778         
32779         var formData = new FormData();
32780
32781         formData.append('returnHTML', 'NO');
32782         
32783         formData.append('crop', crop);
32784         
32785         if(typeof(file.filename) != 'undefined'){
32786             formData.append('filename', file.filename);
32787         }
32788         
32789         if(typeof(file.mimetype) != 'undefined'){
32790             formData.append('mimetype', file.mimetype);
32791         }
32792         
32793         Roo.log(formData);
32794         
32795         if(this.fireEvent('prepare', this, formData) != false){
32796             this.xhr.send(formData);
32797         };
32798     }
32799 });
32800
32801 /*
32802 * Licence: LGPL
32803 */
32804
32805 /**
32806  * @class Roo.bootstrap.DocumentViewer
32807  * @extends Roo.bootstrap.Component
32808  * Bootstrap DocumentViewer class
32809  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32810  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32811  * 
32812  * @constructor
32813  * Create a new DocumentViewer
32814  * @param {Object} config The config object
32815  */
32816
32817 Roo.bootstrap.DocumentViewer = function(config){
32818     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32819     
32820     this.addEvents({
32821         /**
32822          * @event initial
32823          * Fire after initEvent
32824          * @param {Roo.bootstrap.DocumentViewer} this
32825          */
32826         "initial" : true,
32827         /**
32828          * @event click
32829          * Fire after click
32830          * @param {Roo.bootstrap.DocumentViewer} this
32831          */
32832         "click" : true,
32833         /**
32834          * @event download
32835          * Fire after download button
32836          * @param {Roo.bootstrap.DocumentViewer} this
32837          */
32838         "download" : true,
32839         /**
32840          * @event trash
32841          * Fire after trash button
32842          * @param {Roo.bootstrap.DocumentViewer} this
32843          */
32844         "trash" : true
32845         
32846     });
32847 };
32848
32849 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32850     
32851     showDownload : true,
32852     
32853     showTrash : true,
32854     
32855     getAutoCreate : function()
32856     {
32857         var cfg = {
32858             tag : 'div',
32859             cls : 'roo-document-viewer',
32860             cn : [
32861                 {
32862                     tag : 'div',
32863                     cls : 'roo-document-viewer-body',
32864                     cn : [
32865                         {
32866                             tag : 'div',
32867                             cls : 'roo-document-viewer-thumb',
32868                             cn : [
32869                                 {
32870                                     tag : 'img',
32871                                     cls : 'roo-document-viewer-image'
32872                                 }
32873                             ]
32874                         }
32875                     ]
32876                 },
32877                 {
32878                     tag : 'div',
32879                     cls : 'roo-document-viewer-footer',
32880                     cn : {
32881                         tag : 'div',
32882                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32883                         cn : [
32884                             {
32885                                 tag : 'div',
32886                                 cls : 'btn-group roo-document-viewer-download',
32887                                 cn : [
32888                                     {
32889                                         tag : 'button',
32890                                         cls : 'btn btn-default',
32891                                         html : '<i class="fa fa-download"></i>'
32892                                     }
32893                                 ]
32894                             },
32895                             {
32896                                 tag : 'div',
32897                                 cls : 'btn-group roo-document-viewer-trash',
32898                                 cn : [
32899                                     {
32900                                         tag : 'button',
32901                                         cls : 'btn btn-default',
32902                                         html : '<i class="fa fa-trash"></i>'
32903                                     }
32904                                 ]
32905                             }
32906                         ]
32907                     }
32908                 }
32909             ]
32910         };
32911         
32912         return cfg;
32913     },
32914     
32915     initEvents : function()
32916     {
32917         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32918         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32919         
32920         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32921         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32922         
32923         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32924         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32925         
32926         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32927         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32928         
32929         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32930         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32931         
32932         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32933         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32934         
32935         this.bodyEl.on('click', this.onClick, this);
32936         this.downloadBtn.on('click', this.onDownload, this);
32937         this.trashBtn.on('click', this.onTrash, this);
32938         
32939         this.downloadBtn.hide();
32940         this.trashBtn.hide();
32941         
32942         if(this.showDownload){
32943             this.downloadBtn.show();
32944         }
32945         
32946         if(this.showTrash){
32947             this.trashBtn.show();
32948         }
32949         
32950         if(!this.showDownload && !this.showTrash) {
32951             this.footerEl.hide();
32952         }
32953         
32954     },
32955     
32956     initial : function()
32957     {
32958         this.fireEvent('initial', this);
32959         
32960     },
32961     
32962     onClick : function(e)
32963     {
32964         e.preventDefault();
32965         
32966         this.fireEvent('click', this);
32967     },
32968     
32969     onDownload : function(e)
32970     {
32971         e.preventDefault();
32972         
32973         this.fireEvent('download', this);
32974     },
32975     
32976     onTrash : function(e)
32977     {
32978         e.preventDefault();
32979         
32980         this.fireEvent('trash', this);
32981     }
32982     
32983 });
32984 /*
32985  * - LGPL
32986  *
32987  * nav progress bar
32988  * 
32989  */
32990
32991 /**
32992  * @class Roo.bootstrap.NavProgressBar
32993  * @extends Roo.bootstrap.Component
32994  * Bootstrap NavProgressBar class
32995  * 
32996  * @constructor
32997  * Create a new nav progress bar
32998  * @param {Object} config The config object
32999  */
33000
33001 Roo.bootstrap.NavProgressBar = function(config){
33002     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33003
33004     this.bullets = this.bullets || [];
33005    
33006 //    Roo.bootstrap.NavProgressBar.register(this);
33007      this.addEvents({
33008         /**
33009              * @event changed
33010              * Fires when the active item changes
33011              * @param {Roo.bootstrap.NavProgressBar} this
33012              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33013              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33014          */
33015         'changed': true
33016      });
33017     
33018 };
33019
33020 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33021     
33022     bullets : [],
33023     barItems : [],
33024     
33025     getAutoCreate : function()
33026     {
33027         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33028         
33029         cfg = {
33030             tag : 'div',
33031             cls : 'roo-navigation-bar-group',
33032             cn : [
33033                 {
33034                     tag : 'div',
33035                     cls : 'roo-navigation-top-bar'
33036                 },
33037                 {
33038                     tag : 'div',
33039                     cls : 'roo-navigation-bullets-bar',
33040                     cn : [
33041                         {
33042                             tag : 'ul',
33043                             cls : 'roo-navigation-bar'
33044                         }
33045                     ]
33046                 },
33047                 
33048                 {
33049                     tag : 'div',
33050                     cls : 'roo-navigation-bottom-bar'
33051                 }
33052             ]
33053             
33054         };
33055         
33056         return cfg;
33057         
33058     },
33059     
33060     initEvents: function() 
33061     {
33062         
33063     },
33064     
33065     onRender : function(ct, position) 
33066     {
33067         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33068         
33069         if(this.bullets.length){
33070             Roo.each(this.bullets, function(b){
33071                this.addItem(b);
33072             }, this);
33073         }
33074         
33075         this.format();
33076         
33077     },
33078     
33079     addItem : function(cfg)
33080     {
33081         var item = new Roo.bootstrap.NavProgressItem(cfg);
33082         
33083         item.parentId = this.id;
33084         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33085         
33086         if(cfg.html){
33087             var top = new Roo.bootstrap.Element({
33088                 tag : 'div',
33089                 cls : 'roo-navigation-bar-text'
33090             });
33091             
33092             var bottom = new Roo.bootstrap.Element({
33093                 tag : 'div',
33094                 cls : 'roo-navigation-bar-text'
33095             });
33096             
33097             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33098             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33099             
33100             var topText = new Roo.bootstrap.Element({
33101                 tag : 'span',
33102                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33103             });
33104             
33105             var bottomText = new Roo.bootstrap.Element({
33106                 tag : 'span',
33107                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33108             });
33109             
33110             topText.onRender(top.el, null);
33111             bottomText.onRender(bottom.el, null);
33112             
33113             item.topEl = top;
33114             item.bottomEl = bottom;
33115         }
33116         
33117         this.barItems.push(item);
33118         
33119         return item;
33120     },
33121     
33122     getActive : function()
33123     {
33124         var active = false;
33125         
33126         Roo.each(this.barItems, function(v){
33127             
33128             if (!v.isActive()) {
33129                 return;
33130             }
33131             
33132             active = v;
33133             return false;
33134             
33135         });
33136         
33137         return active;
33138     },
33139     
33140     setActiveItem : function(item)
33141     {
33142         var prev = false;
33143         
33144         Roo.each(this.barItems, function(v){
33145             if (v.rid == item.rid) {
33146                 return ;
33147             }
33148             
33149             if (v.isActive()) {
33150                 v.setActive(false);
33151                 prev = v;
33152             }
33153         });
33154
33155         item.setActive(true);
33156         
33157         this.fireEvent('changed', this, item, prev);
33158     },
33159     
33160     getBarItem: function(rid)
33161     {
33162         var ret = false;
33163         
33164         Roo.each(this.barItems, function(e) {
33165             if (e.rid != rid) {
33166                 return;
33167             }
33168             
33169             ret =  e;
33170             return false;
33171         });
33172         
33173         return ret;
33174     },
33175     
33176     indexOfItem : function(item)
33177     {
33178         var index = false;
33179         
33180         Roo.each(this.barItems, function(v, i){
33181             
33182             if (v.rid != item.rid) {
33183                 return;
33184             }
33185             
33186             index = i;
33187             return false
33188         });
33189         
33190         return index;
33191     },
33192     
33193     setActiveNext : function()
33194     {
33195         var i = this.indexOfItem(this.getActive());
33196         
33197         if (i > this.barItems.length) {
33198             return;
33199         }
33200         
33201         this.setActiveItem(this.barItems[i+1]);
33202     },
33203     
33204     setActivePrev : function()
33205     {
33206         var i = this.indexOfItem(this.getActive());
33207         
33208         if (i  < 1) {
33209             return;
33210         }
33211         
33212         this.setActiveItem(this.barItems[i-1]);
33213     },
33214     
33215     format : function()
33216     {
33217         if(!this.barItems.length){
33218             return;
33219         }
33220      
33221         var width = 100 / this.barItems.length;
33222         
33223         Roo.each(this.barItems, function(i){
33224             i.el.setStyle('width', width + '%');
33225             i.topEl.el.setStyle('width', width + '%');
33226             i.bottomEl.el.setStyle('width', width + '%');
33227         }, this);
33228         
33229     }
33230     
33231 });
33232 /*
33233  * - LGPL
33234  *
33235  * Nav Progress Item
33236  * 
33237  */
33238
33239 /**
33240  * @class Roo.bootstrap.NavProgressItem
33241  * @extends Roo.bootstrap.Component
33242  * Bootstrap NavProgressItem class
33243  * @cfg {String} rid the reference id
33244  * @cfg {Boolean} active (true|false) Is item active default false
33245  * @cfg {Boolean} disabled (true|false) Is item active default false
33246  * @cfg {String} html
33247  * @cfg {String} position (top|bottom) text position default bottom
33248  * @cfg {String} icon show icon instead of number
33249  * 
33250  * @constructor
33251  * Create a new NavProgressItem
33252  * @param {Object} config The config object
33253  */
33254 Roo.bootstrap.NavProgressItem = function(config){
33255     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33256     this.addEvents({
33257         // raw events
33258         /**
33259          * @event click
33260          * The raw click event for the entire grid.
33261          * @param {Roo.bootstrap.NavProgressItem} this
33262          * @param {Roo.EventObject} e
33263          */
33264         "click" : true
33265     });
33266    
33267 };
33268
33269 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33270     
33271     rid : '',
33272     active : false,
33273     disabled : false,
33274     html : '',
33275     position : 'bottom',
33276     icon : false,
33277     
33278     getAutoCreate : function()
33279     {
33280         var iconCls = 'roo-navigation-bar-item-icon';
33281         
33282         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33283         
33284         var cfg = {
33285             tag: 'li',
33286             cls: 'roo-navigation-bar-item',
33287             cn : [
33288                 {
33289                     tag : 'i',
33290                     cls : iconCls
33291                 }
33292             ]
33293         };
33294         
33295         if(this.active){
33296             cfg.cls += ' active';
33297         }
33298         if(this.disabled){
33299             cfg.cls += ' disabled';
33300         }
33301         
33302         return cfg;
33303     },
33304     
33305     disable : function()
33306     {
33307         this.setDisabled(true);
33308     },
33309     
33310     enable : function()
33311     {
33312         this.setDisabled(false);
33313     },
33314     
33315     initEvents: function() 
33316     {
33317         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33318         
33319         this.iconEl.on('click', this.onClick, this);
33320     },
33321     
33322     onClick : function(e)
33323     {
33324         e.preventDefault();
33325         
33326         if(this.disabled){
33327             return;
33328         }
33329         
33330         if(this.fireEvent('click', this, e) === false){
33331             return;
33332         };
33333         
33334         this.parent().setActiveItem(this);
33335     },
33336     
33337     isActive: function () 
33338     {
33339         return this.active;
33340     },
33341     
33342     setActive : function(state)
33343     {
33344         if(this.active == state){
33345             return;
33346         }
33347         
33348         this.active = state;
33349         
33350         if (state) {
33351             this.el.addClass('active');
33352             return;
33353         }
33354         
33355         this.el.removeClass('active');
33356         
33357         return;
33358     },
33359     
33360     setDisabled : function(state)
33361     {
33362         if(this.disabled == state){
33363             return;
33364         }
33365         
33366         this.disabled = state;
33367         
33368         if (state) {
33369             this.el.addClass('disabled');
33370             return;
33371         }
33372         
33373         this.el.removeClass('disabled');
33374     },
33375     
33376     tooltipEl : function()
33377     {
33378         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33379     }
33380 });
33381  
33382
33383  /*
33384  * - LGPL
33385  *
33386  * FieldLabel
33387  * 
33388  */
33389
33390 /**
33391  * @class Roo.bootstrap.FieldLabel
33392  * @extends Roo.bootstrap.Component
33393  * Bootstrap FieldLabel class
33394  * @cfg {String} html contents of the element
33395  * @cfg {String} tag tag of the element default label
33396  * @cfg {String} cls class of the element
33397  * @cfg {String} target label target 
33398  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33399  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33400  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33401  * @cfg {String} iconTooltip default "This field is required"
33402  * @cfg {String} indicatorpos (left|right) default left
33403  * 
33404  * @constructor
33405  * Create a new FieldLabel
33406  * @param {Object} config The config object
33407  */
33408
33409 Roo.bootstrap.FieldLabel = function(config){
33410     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33411     
33412     this.addEvents({
33413             /**
33414              * @event invalid
33415              * Fires after the field has been marked as invalid.
33416              * @param {Roo.form.FieldLabel} this
33417              * @param {String} msg The validation message
33418              */
33419             invalid : true,
33420             /**
33421              * @event valid
33422              * Fires after the field has been validated with no errors.
33423              * @param {Roo.form.FieldLabel} this
33424              */
33425             valid : true
33426         });
33427 };
33428
33429 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33430     
33431     tag: 'label',
33432     cls: '',
33433     html: '',
33434     target: '',
33435     allowBlank : true,
33436     invalidClass : 'has-warning',
33437     validClass : 'has-success',
33438     iconTooltip : 'This field is required',
33439     indicatorpos : 'left',
33440     
33441     getAutoCreate : function(){
33442         
33443         var cls = "";
33444         if (!this.allowBlank) {
33445             cls  = "visible";
33446         }
33447         
33448         var cfg = {
33449             tag : this.tag,
33450             cls : 'roo-bootstrap-field-label ' + this.cls,
33451             for : this.target,
33452             cn : [
33453                 {
33454                     tag : 'i',
33455                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33456                     tooltip : this.iconTooltip
33457                 },
33458                 {
33459                     tag : 'span',
33460                     html : this.html
33461                 }
33462             ] 
33463         };
33464         
33465         if(this.indicatorpos == 'right'){
33466             var cfg = {
33467                 tag : this.tag,
33468                 cls : 'roo-bootstrap-field-label ' + this.cls,
33469                 for : this.target,
33470                 cn : [
33471                     {
33472                         tag : 'span',
33473                         html : this.html
33474                     },
33475                     {
33476                         tag : 'i',
33477                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33478                         tooltip : this.iconTooltip
33479                     }
33480                 ] 
33481             };
33482         }
33483         
33484         return cfg;
33485     },
33486     
33487     initEvents: function() 
33488     {
33489         Roo.bootstrap.Element.superclass.initEvents.call(this);
33490         
33491         this.indicator = this.indicatorEl();
33492         
33493         if(this.indicator){
33494             this.indicator.removeClass('visible');
33495             this.indicator.addClass('invisible');
33496         }
33497         
33498         Roo.bootstrap.FieldLabel.register(this);
33499     },
33500     
33501     indicatorEl : function()
33502     {
33503         var indicator = this.el.select('i.roo-required-indicator',true).first();
33504         
33505         if(!indicator){
33506             return false;
33507         }
33508         
33509         return indicator;
33510         
33511     },
33512     
33513     /**
33514      * Mark this field as valid
33515      */
33516     markValid : function()
33517     {
33518         if(this.indicator){
33519             this.indicator.removeClass('visible');
33520             this.indicator.addClass('invisible');
33521         }
33522         if (Roo.bootstrap.version == 3) {
33523             this.el.removeClass(this.invalidClass);
33524             this.el.addClass(this.validClass);
33525         } else {
33526             this.el.removeClass('is-invalid');
33527             this.el.addClass('is-valid');
33528         }
33529         
33530         
33531         this.fireEvent('valid', this);
33532     },
33533     
33534     /**
33535      * Mark this field as invalid
33536      * @param {String} msg The validation message
33537      */
33538     markInvalid : function(msg)
33539     {
33540         if(this.indicator){
33541             this.indicator.removeClass('invisible');
33542             this.indicator.addClass('visible');
33543         }
33544           if (Roo.bootstrap.version == 3) {
33545             this.el.removeClass(this.validClass);
33546             this.el.addClass(this.invalidClass);
33547         } else {
33548             this.el.removeClass('is-valid');
33549             this.el.addClass('is-invalid');
33550         }
33551         
33552         
33553         this.fireEvent('invalid', this, msg);
33554     }
33555     
33556    
33557 });
33558
33559 Roo.apply(Roo.bootstrap.FieldLabel, {
33560     
33561     groups: {},
33562     
33563      /**
33564     * register a FieldLabel Group
33565     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33566     */
33567     register : function(label)
33568     {
33569         if(this.groups.hasOwnProperty(label.target)){
33570             return;
33571         }
33572      
33573         this.groups[label.target] = label;
33574         
33575     },
33576     /**
33577     * fetch a FieldLabel Group based on the target
33578     * @param {string} target
33579     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33580     */
33581     get: function(target) {
33582         if (typeof(this.groups[target]) == 'undefined') {
33583             return false;
33584         }
33585         
33586         return this.groups[target] ;
33587     }
33588 });
33589
33590  
33591
33592  /*
33593  * - LGPL
33594  *
33595  * page DateSplitField.
33596  * 
33597  */
33598
33599
33600 /**
33601  * @class Roo.bootstrap.DateSplitField
33602  * @extends Roo.bootstrap.Component
33603  * Bootstrap DateSplitField class
33604  * @cfg {string} fieldLabel - the label associated
33605  * @cfg {Number} labelWidth set the width of label (0-12)
33606  * @cfg {String} labelAlign (top|left)
33607  * @cfg {Boolean} dayAllowBlank (true|false) default false
33608  * @cfg {Boolean} monthAllowBlank (true|false) default false
33609  * @cfg {Boolean} yearAllowBlank (true|false) default false
33610  * @cfg {string} dayPlaceholder 
33611  * @cfg {string} monthPlaceholder
33612  * @cfg {string} yearPlaceholder
33613  * @cfg {string} dayFormat default 'd'
33614  * @cfg {string} monthFormat default 'm'
33615  * @cfg {string} yearFormat default 'Y'
33616  * @cfg {Number} labellg set the width of label (1-12)
33617  * @cfg {Number} labelmd set the width of label (1-12)
33618  * @cfg {Number} labelsm set the width of label (1-12)
33619  * @cfg {Number} labelxs set the width of label (1-12)
33620
33621  *     
33622  * @constructor
33623  * Create a new DateSplitField
33624  * @param {Object} config The config object
33625  */
33626
33627 Roo.bootstrap.DateSplitField = function(config){
33628     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33629     
33630     this.addEvents({
33631         // raw events
33632          /**
33633          * @event years
33634          * getting the data of years
33635          * @param {Roo.bootstrap.DateSplitField} this
33636          * @param {Object} years
33637          */
33638         "years" : true,
33639         /**
33640          * @event days
33641          * getting the data of days
33642          * @param {Roo.bootstrap.DateSplitField} this
33643          * @param {Object} days
33644          */
33645         "days" : true,
33646         /**
33647          * @event invalid
33648          * Fires after the field has been marked as invalid.
33649          * @param {Roo.form.Field} this
33650          * @param {String} msg The validation message
33651          */
33652         invalid : true,
33653        /**
33654          * @event valid
33655          * Fires after the field has been validated with no errors.
33656          * @param {Roo.form.Field} this
33657          */
33658         valid : true
33659     });
33660 };
33661
33662 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33663     
33664     fieldLabel : '',
33665     labelAlign : 'top',
33666     labelWidth : 3,
33667     dayAllowBlank : false,
33668     monthAllowBlank : false,
33669     yearAllowBlank : false,
33670     dayPlaceholder : '',
33671     monthPlaceholder : '',
33672     yearPlaceholder : '',
33673     dayFormat : 'd',
33674     monthFormat : 'm',
33675     yearFormat : 'Y',
33676     isFormField : true,
33677     labellg : 0,
33678     labelmd : 0,
33679     labelsm : 0,
33680     labelxs : 0,
33681     
33682     getAutoCreate : function()
33683     {
33684         var cfg = {
33685             tag : 'div',
33686             cls : 'row roo-date-split-field-group',
33687             cn : [
33688                 {
33689                     tag : 'input',
33690                     type : 'hidden',
33691                     cls : 'form-hidden-field roo-date-split-field-group-value',
33692                     name : this.name
33693                 }
33694             ]
33695         };
33696         
33697         var labelCls = 'col-md-12';
33698         var contentCls = 'col-md-4';
33699         
33700         if(this.fieldLabel){
33701             
33702             var label = {
33703                 tag : 'div',
33704                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33705                 cn : [
33706                     {
33707                         tag : 'label',
33708                         html : this.fieldLabel
33709                     }
33710                 ]
33711             };
33712             
33713             if(this.labelAlign == 'left'){
33714             
33715                 if(this.labelWidth > 12){
33716                     label.style = "width: " + this.labelWidth + 'px';
33717                 }
33718
33719                 if(this.labelWidth < 13 && this.labelmd == 0){
33720                     this.labelmd = this.labelWidth;
33721                 }
33722
33723                 if(this.labellg > 0){
33724                     labelCls = ' col-lg-' + this.labellg;
33725                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33726                 }
33727
33728                 if(this.labelmd > 0){
33729                     labelCls = ' col-md-' + this.labelmd;
33730                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33731                 }
33732
33733                 if(this.labelsm > 0){
33734                     labelCls = ' col-sm-' + this.labelsm;
33735                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33736                 }
33737
33738                 if(this.labelxs > 0){
33739                     labelCls = ' col-xs-' + this.labelxs;
33740                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33741                 }
33742             }
33743             
33744             label.cls += ' ' + labelCls;
33745             
33746             cfg.cn.push(label);
33747         }
33748         
33749         Roo.each(['day', 'month', 'year'], function(t){
33750             cfg.cn.push({
33751                 tag : 'div',
33752                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33753             });
33754         }, this);
33755         
33756         return cfg;
33757     },
33758     
33759     inputEl: function ()
33760     {
33761         return this.el.select('.roo-date-split-field-group-value', true).first();
33762     },
33763     
33764     onRender : function(ct, position) 
33765     {
33766         var _this = this;
33767         
33768         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33769         
33770         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33771         
33772         this.dayField = new Roo.bootstrap.ComboBox({
33773             allowBlank : this.dayAllowBlank,
33774             alwaysQuery : true,
33775             displayField : 'value',
33776             editable : false,
33777             fieldLabel : '',
33778             forceSelection : true,
33779             mode : 'local',
33780             placeholder : this.dayPlaceholder,
33781             selectOnFocus : true,
33782             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33783             triggerAction : 'all',
33784             typeAhead : true,
33785             valueField : 'value',
33786             store : new Roo.data.SimpleStore({
33787                 data : (function() {    
33788                     var days = [];
33789                     _this.fireEvent('days', _this, days);
33790                     return days;
33791                 })(),
33792                 fields : [ 'value' ]
33793             }),
33794             listeners : {
33795                 select : function (_self, record, index)
33796                 {
33797                     _this.setValue(_this.getValue());
33798                 }
33799             }
33800         });
33801
33802         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33803         
33804         this.monthField = new Roo.bootstrap.MonthField({
33805             after : '<i class=\"fa fa-calendar\"></i>',
33806             allowBlank : this.monthAllowBlank,
33807             placeholder : this.monthPlaceholder,
33808             readOnly : true,
33809             listeners : {
33810                 render : function (_self)
33811                 {
33812                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33813                         e.preventDefault();
33814                         _self.focus();
33815                     });
33816                 },
33817                 select : function (_self, oldvalue, newvalue)
33818                 {
33819                     _this.setValue(_this.getValue());
33820                 }
33821             }
33822         });
33823         
33824         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33825         
33826         this.yearField = new Roo.bootstrap.ComboBox({
33827             allowBlank : this.yearAllowBlank,
33828             alwaysQuery : true,
33829             displayField : 'value',
33830             editable : false,
33831             fieldLabel : '',
33832             forceSelection : true,
33833             mode : 'local',
33834             placeholder : this.yearPlaceholder,
33835             selectOnFocus : true,
33836             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33837             triggerAction : 'all',
33838             typeAhead : true,
33839             valueField : 'value',
33840             store : new Roo.data.SimpleStore({
33841                 data : (function() {
33842                     var years = [];
33843                     _this.fireEvent('years', _this, years);
33844                     return years;
33845                 })(),
33846                 fields : [ 'value' ]
33847             }),
33848             listeners : {
33849                 select : function (_self, record, index)
33850                 {
33851                     _this.setValue(_this.getValue());
33852                 }
33853             }
33854         });
33855
33856         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33857     },
33858     
33859     setValue : function(v, format)
33860     {
33861         this.inputEl.dom.value = v;
33862         
33863         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33864         
33865         var d = Date.parseDate(v, f);
33866         
33867         if(!d){
33868             this.validate();
33869             return;
33870         }
33871         
33872         this.setDay(d.format(this.dayFormat));
33873         this.setMonth(d.format(this.monthFormat));
33874         this.setYear(d.format(this.yearFormat));
33875         
33876         this.validate();
33877         
33878         return;
33879     },
33880     
33881     setDay : function(v)
33882     {
33883         this.dayField.setValue(v);
33884         this.inputEl.dom.value = this.getValue();
33885         this.validate();
33886         return;
33887     },
33888     
33889     setMonth : function(v)
33890     {
33891         this.monthField.setValue(v, true);
33892         this.inputEl.dom.value = this.getValue();
33893         this.validate();
33894         return;
33895     },
33896     
33897     setYear : function(v)
33898     {
33899         this.yearField.setValue(v);
33900         this.inputEl.dom.value = this.getValue();
33901         this.validate();
33902         return;
33903     },
33904     
33905     getDay : function()
33906     {
33907         return this.dayField.getValue();
33908     },
33909     
33910     getMonth : function()
33911     {
33912         return this.monthField.getValue();
33913     },
33914     
33915     getYear : function()
33916     {
33917         return this.yearField.getValue();
33918     },
33919     
33920     getValue : function()
33921     {
33922         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33923         
33924         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33925         
33926         return date;
33927     },
33928     
33929     reset : function()
33930     {
33931         this.setDay('');
33932         this.setMonth('');
33933         this.setYear('');
33934         this.inputEl.dom.value = '';
33935         this.validate();
33936         return;
33937     },
33938     
33939     validate : function()
33940     {
33941         var d = this.dayField.validate();
33942         var m = this.monthField.validate();
33943         var y = this.yearField.validate();
33944         
33945         var valid = true;
33946         
33947         if(
33948                 (!this.dayAllowBlank && !d) ||
33949                 (!this.monthAllowBlank && !m) ||
33950                 (!this.yearAllowBlank && !y)
33951         ){
33952             valid = false;
33953         }
33954         
33955         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33956             return valid;
33957         }
33958         
33959         if(valid){
33960             this.markValid();
33961             return valid;
33962         }
33963         
33964         this.markInvalid();
33965         
33966         return valid;
33967     },
33968     
33969     markValid : function()
33970     {
33971         
33972         var label = this.el.select('label', true).first();
33973         var icon = this.el.select('i.fa-star', true).first();
33974
33975         if(label && icon){
33976             icon.remove();
33977         }
33978         
33979         this.fireEvent('valid', this);
33980     },
33981     
33982      /**
33983      * Mark this field as invalid
33984      * @param {String} msg The validation message
33985      */
33986     markInvalid : function(msg)
33987     {
33988         
33989         var label = this.el.select('label', true).first();
33990         var icon = this.el.select('i.fa-star', true).first();
33991
33992         if(label && !icon){
33993             this.el.select('.roo-date-split-field-label', true).createChild({
33994                 tag : 'i',
33995                 cls : 'text-danger fa fa-lg fa-star',
33996                 tooltip : 'This field is required',
33997                 style : 'margin-right:5px;'
33998             }, label, true);
33999         }
34000         
34001         this.fireEvent('invalid', this, msg);
34002     },
34003     
34004     clearInvalid : function()
34005     {
34006         var label = this.el.select('label', true).first();
34007         var icon = this.el.select('i.fa-star', true).first();
34008
34009         if(label && icon){
34010             icon.remove();
34011         }
34012         
34013         this.fireEvent('valid', this);
34014     },
34015     
34016     getName: function()
34017     {
34018         return this.name;
34019     }
34020     
34021 });
34022
34023  /**
34024  *
34025  * This is based on 
34026  * http://masonry.desandro.com
34027  *
34028  * The idea is to render all the bricks based on vertical width...
34029  *
34030  * The original code extends 'outlayer' - we might need to use that....
34031  * 
34032  */
34033
34034
34035 /**
34036  * @class Roo.bootstrap.LayoutMasonry
34037  * @extends Roo.bootstrap.Component
34038  * Bootstrap Layout Masonry class
34039  * 
34040  * @constructor
34041  * Create a new Element
34042  * @param {Object} config The config object
34043  */
34044
34045 Roo.bootstrap.LayoutMasonry = function(config){
34046     
34047     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34048     
34049     this.bricks = [];
34050     
34051     Roo.bootstrap.LayoutMasonry.register(this);
34052     
34053     this.addEvents({
34054         // raw events
34055         /**
34056          * @event layout
34057          * Fire after layout the items
34058          * @param {Roo.bootstrap.LayoutMasonry} this
34059          * @param {Roo.EventObject} e
34060          */
34061         "layout" : true
34062     });
34063     
34064 };
34065
34066 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34067     
34068     /**
34069      * @cfg {Boolean} isLayoutInstant = no animation?
34070      */   
34071     isLayoutInstant : false, // needed?
34072    
34073     /**
34074      * @cfg {Number} boxWidth  width of the columns
34075      */   
34076     boxWidth : 450,
34077     
34078       /**
34079      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34080      */   
34081     boxHeight : 0,
34082     
34083     /**
34084      * @cfg {Number} padWidth padding below box..
34085      */   
34086     padWidth : 10, 
34087     
34088     /**
34089      * @cfg {Number} gutter gutter width..
34090      */   
34091     gutter : 10,
34092     
34093      /**
34094      * @cfg {Number} maxCols maximum number of columns
34095      */   
34096     
34097     maxCols: 0,
34098     
34099     /**
34100      * @cfg {Boolean} isAutoInitial defalut true
34101      */   
34102     isAutoInitial : true, 
34103     
34104     containerWidth: 0,
34105     
34106     /**
34107      * @cfg {Boolean} isHorizontal defalut false
34108      */   
34109     isHorizontal : false, 
34110
34111     currentSize : null,
34112     
34113     tag: 'div',
34114     
34115     cls: '',
34116     
34117     bricks: null, //CompositeElement
34118     
34119     cols : 1,
34120     
34121     _isLayoutInited : false,
34122     
34123 //    isAlternative : false, // only use for vertical layout...
34124     
34125     /**
34126      * @cfg {Number} alternativePadWidth padding below box..
34127      */   
34128     alternativePadWidth : 50,
34129     
34130     selectedBrick : [],
34131     
34132     getAutoCreate : function(){
34133         
34134         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34135         
34136         var cfg = {
34137             tag: this.tag,
34138             cls: 'blog-masonary-wrapper ' + this.cls,
34139             cn : {
34140                 cls : 'mas-boxes masonary'
34141             }
34142         };
34143         
34144         return cfg;
34145     },
34146     
34147     getChildContainer: function( )
34148     {
34149         if (this.boxesEl) {
34150             return this.boxesEl;
34151         }
34152         
34153         this.boxesEl = this.el.select('.mas-boxes').first();
34154         
34155         return this.boxesEl;
34156     },
34157     
34158     
34159     initEvents : function()
34160     {
34161         var _this = this;
34162         
34163         if(this.isAutoInitial){
34164             Roo.log('hook children rendered');
34165             this.on('childrenrendered', function() {
34166                 Roo.log('children rendered');
34167                 _this.initial();
34168             } ,this);
34169         }
34170     },
34171     
34172     initial : function()
34173     {
34174         this.selectedBrick = [];
34175         
34176         this.currentSize = this.el.getBox(true);
34177         
34178         Roo.EventManager.onWindowResize(this.resize, this); 
34179
34180         if(!this.isAutoInitial){
34181             this.layout();
34182             return;
34183         }
34184         
34185         this.layout();
34186         
34187         return;
34188         //this.layout.defer(500,this);
34189         
34190     },
34191     
34192     resize : function()
34193     {
34194         var cs = this.el.getBox(true);
34195         
34196         if (
34197                 this.currentSize.width == cs.width && 
34198                 this.currentSize.x == cs.x && 
34199                 this.currentSize.height == cs.height && 
34200                 this.currentSize.y == cs.y 
34201         ) {
34202             Roo.log("no change in with or X or Y");
34203             return;
34204         }
34205         
34206         this.currentSize = cs;
34207         
34208         this.layout();
34209         
34210     },
34211     
34212     layout : function()
34213     {   
34214         this._resetLayout();
34215         
34216         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34217         
34218         this.layoutItems( isInstant );
34219       
34220         this._isLayoutInited = true;
34221         
34222         this.fireEvent('layout', this);
34223         
34224     },
34225     
34226     _resetLayout : function()
34227     {
34228         if(this.isHorizontal){
34229             this.horizontalMeasureColumns();
34230             return;
34231         }
34232         
34233         this.verticalMeasureColumns();
34234         
34235     },
34236     
34237     verticalMeasureColumns : function()
34238     {
34239         this.getContainerWidth();
34240         
34241 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34242 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34243 //            return;
34244 //        }
34245         
34246         var boxWidth = this.boxWidth + this.padWidth;
34247         
34248         if(this.containerWidth < this.boxWidth){
34249             boxWidth = this.containerWidth
34250         }
34251         
34252         var containerWidth = this.containerWidth;
34253         
34254         var cols = Math.floor(containerWidth / boxWidth);
34255         
34256         this.cols = Math.max( cols, 1 );
34257         
34258         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34259         
34260         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34261         
34262         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34263         
34264         this.colWidth = boxWidth + avail - this.padWidth;
34265         
34266         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34267         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34268     },
34269     
34270     horizontalMeasureColumns : function()
34271     {
34272         this.getContainerWidth();
34273         
34274         var boxWidth = this.boxWidth;
34275         
34276         if(this.containerWidth < boxWidth){
34277             boxWidth = this.containerWidth;
34278         }
34279         
34280         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34281         
34282         this.el.setHeight(boxWidth);
34283         
34284     },
34285     
34286     getContainerWidth : function()
34287     {
34288         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34289     },
34290     
34291     layoutItems : function( isInstant )
34292     {
34293         Roo.log(this.bricks);
34294         
34295         var items = Roo.apply([], this.bricks);
34296         
34297         if(this.isHorizontal){
34298             this._horizontalLayoutItems( items , isInstant );
34299             return;
34300         }
34301         
34302 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34303 //            this._verticalAlternativeLayoutItems( items , isInstant );
34304 //            return;
34305 //        }
34306         
34307         this._verticalLayoutItems( items , isInstant );
34308         
34309     },
34310     
34311     _verticalLayoutItems : function ( items , isInstant)
34312     {
34313         if ( !items || !items.length ) {
34314             return;
34315         }
34316         
34317         var standard = [
34318             ['xs', 'xs', 'xs', 'tall'],
34319             ['xs', 'xs', 'tall'],
34320             ['xs', 'xs', 'sm'],
34321             ['xs', 'xs', 'xs'],
34322             ['xs', 'tall'],
34323             ['xs', 'sm'],
34324             ['xs', 'xs'],
34325             ['xs'],
34326             
34327             ['sm', 'xs', 'xs'],
34328             ['sm', 'xs'],
34329             ['sm'],
34330             
34331             ['tall', 'xs', 'xs', 'xs'],
34332             ['tall', 'xs', 'xs'],
34333             ['tall', 'xs'],
34334             ['tall']
34335             
34336         ];
34337         
34338         var queue = [];
34339         
34340         var boxes = [];
34341         
34342         var box = [];
34343         
34344         Roo.each(items, function(item, k){
34345             
34346             switch (item.size) {
34347                 // these layouts take up a full box,
34348                 case 'md' :
34349                 case 'md-left' :
34350                 case 'md-right' :
34351                 case 'wide' :
34352                     
34353                     if(box.length){
34354                         boxes.push(box);
34355                         box = [];
34356                     }
34357                     
34358                     boxes.push([item]);
34359                     
34360                     break;
34361                     
34362                 case 'xs' :
34363                 case 'sm' :
34364                 case 'tall' :
34365                     
34366                     box.push(item);
34367                     
34368                     break;
34369                 default :
34370                     break;
34371                     
34372             }
34373             
34374         }, this);
34375         
34376         if(box.length){
34377             boxes.push(box);
34378             box = [];
34379         }
34380         
34381         var filterPattern = function(box, length)
34382         {
34383             if(!box.length){
34384                 return;
34385             }
34386             
34387             var match = false;
34388             
34389             var pattern = box.slice(0, length);
34390             
34391             var format = [];
34392             
34393             Roo.each(pattern, function(i){
34394                 format.push(i.size);
34395             }, this);
34396             
34397             Roo.each(standard, function(s){
34398                 
34399                 if(String(s) != String(format)){
34400                     return;
34401                 }
34402                 
34403                 match = true;
34404                 return false;
34405                 
34406             }, this);
34407             
34408             if(!match && length == 1){
34409                 return;
34410             }
34411             
34412             if(!match){
34413                 filterPattern(box, length - 1);
34414                 return;
34415             }
34416                 
34417             queue.push(pattern);
34418
34419             box = box.slice(length, box.length);
34420
34421             filterPattern(box, 4);
34422
34423             return;
34424             
34425         }
34426         
34427         Roo.each(boxes, function(box, k){
34428             
34429             if(!box.length){
34430                 return;
34431             }
34432             
34433             if(box.length == 1){
34434                 queue.push(box);
34435                 return;
34436             }
34437             
34438             filterPattern(box, 4);
34439             
34440         }, this);
34441         
34442         this._processVerticalLayoutQueue( queue, isInstant );
34443         
34444     },
34445     
34446 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34447 //    {
34448 //        if ( !items || !items.length ) {
34449 //            return;
34450 //        }
34451 //
34452 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34453 //        
34454 //    },
34455     
34456     _horizontalLayoutItems : function ( items , isInstant)
34457     {
34458         if ( !items || !items.length || items.length < 3) {
34459             return;
34460         }
34461         
34462         items.reverse();
34463         
34464         var eItems = items.slice(0, 3);
34465         
34466         items = items.slice(3, items.length);
34467         
34468         var standard = [
34469             ['xs', 'xs', 'xs', 'wide'],
34470             ['xs', 'xs', 'wide'],
34471             ['xs', 'xs', 'sm'],
34472             ['xs', 'xs', 'xs'],
34473             ['xs', 'wide'],
34474             ['xs', 'sm'],
34475             ['xs', 'xs'],
34476             ['xs'],
34477             
34478             ['sm', 'xs', 'xs'],
34479             ['sm', 'xs'],
34480             ['sm'],
34481             
34482             ['wide', 'xs', 'xs', 'xs'],
34483             ['wide', 'xs', 'xs'],
34484             ['wide', 'xs'],
34485             ['wide'],
34486             
34487             ['wide-thin']
34488         ];
34489         
34490         var queue = [];
34491         
34492         var boxes = [];
34493         
34494         var box = [];
34495         
34496         Roo.each(items, function(item, k){
34497             
34498             switch (item.size) {
34499                 case 'md' :
34500                 case 'md-left' :
34501                 case 'md-right' :
34502                 case 'tall' :
34503                     
34504                     if(box.length){
34505                         boxes.push(box);
34506                         box = [];
34507                     }
34508                     
34509                     boxes.push([item]);
34510                     
34511                     break;
34512                     
34513                 case 'xs' :
34514                 case 'sm' :
34515                 case 'wide' :
34516                 case 'wide-thin' :
34517                     
34518                     box.push(item);
34519                     
34520                     break;
34521                 default :
34522                     break;
34523                     
34524             }
34525             
34526         }, this);
34527         
34528         if(box.length){
34529             boxes.push(box);
34530             box = [];
34531         }
34532         
34533         var filterPattern = function(box, length)
34534         {
34535             if(!box.length){
34536                 return;
34537             }
34538             
34539             var match = false;
34540             
34541             var pattern = box.slice(0, length);
34542             
34543             var format = [];
34544             
34545             Roo.each(pattern, function(i){
34546                 format.push(i.size);
34547             }, this);
34548             
34549             Roo.each(standard, function(s){
34550                 
34551                 if(String(s) != String(format)){
34552                     return;
34553                 }
34554                 
34555                 match = true;
34556                 return false;
34557                 
34558             }, this);
34559             
34560             if(!match && length == 1){
34561                 return;
34562             }
34563             
34564             if(!match){
34565                 filterPattern(box, length - 1);
34566                 return;
34567             }
34568                 
34569             queue.push(pattern);
34570
34571             box = box.slice(length, box.length);
34572
34573             filterPattern(box, 4);
34574
34575             return;
34576             
34577         }
34578         
34579         Roo.each(boxes, function(box, k){
34580             
34581             if(!box.length){
34582                 return;
34583             }
34584             
34585             if(box.length == 1){
34586                 queue.push(box);
34587                 return;
34588             }
34589             
34590             filterPattern(box, 4);
34591             
34592         }, this);
34593         
34594         
34595         var prune = [];
34596         
34597         var pos = this.el.getBox(true);
34598         
34599         var minX = pos.x;
34600         
34601         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34602         
34603         var hit_end = false;
34604         
34605         Roo.each(queue, function(box){
34606             
34607             if(hit_end){
34608                 
34609                 Roo.each(box, function(b){
34610                 
34611                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34612                     b.el.hide();
34613
34614                 }, this);
34615
34616                 return;
34617             }
34618             
34619             var mx = 0;
34620             
34621             Roo.each(box, function(b){
34622                 
34623                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34624                 b.el.show();
34625
34626                 mx = Math.max(mx, b.x);
34627                 
34628             }, this);
34629             
34630             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34631             
34632             if(maxX < minX){
34633                 
34634                 Roo.each(box, function(b){
34635                 
34636                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34637                     b.el.hide();
34638                     
34639                 }, this);
34640                 
34641                 hit_end = true;
34642                 
34643                 return;
34644             }
34645             
34646             prune.push(box);
34647             
34648         }, this);
34649         
34650         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34651     },
34652     
34653     /** Sets position of item in DOM
34654     * @param {Element} item
34655     * @param {Number} x - horizontal position
34656     * @param {Number} y - vertical position
34657     * @param {Boolean} isInstant - disables transitions
34658     */
34659     _processVerticalLayoutQueue : function( queue, isInstant )
34660     {
34661         var pos = this.el.getBox(true);
34662         var x = pos.x;
34663         var y = pos.y;
34664         var maxY = [];
34665         
34666         for (var i = 0; i < this.cols; i++){
34667             maxY[i] = pos.y;
34668         }
34669         
34670         Roo.each(queue, function(box, k){
34671             
34672             var col = k % this.cols;
34673             
34674             Roo.each(box, function(b,kk){
34675                 
34676                 b.el.position('absolute');
34677                 
34678                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34679                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34680                 
34681                 if(b.size == 'md-left' || b.size == 'md-right'){
34682                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34683                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34684                 }
34685                 
34686                 b.el.setWidth(width);
34687                 b.el.setHeight(height);
34688                 // iframe?
34689                 b.el.select('iframe',true).setSize(width,height);
34690                 
34691             }, this);
34692             
34693             for (var i = 0; i < this.cols; i++){
34694                 
34695                 if(maxY[i] < maxY[col]){
34696                     col = i;
34697                     continue;
34698                 }
34699                 
34700                 col = Math.min(col, i);
34701                 
34702             }
34703             
34704             x = pos.x + col * (this.colWidth + this.padWidth);
34705             
34706             y = maxY[col];
34707             
34708             var positions = [];
34709             
34710             switch (box.length){
34711                 case 1 :
34712                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34713                     break;
34714                 case 2 :
34715                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34716                     break;
34717                 case 3 :
34718                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34719                     break;
34720                 case 4 :
34721                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34722                     break;
34723                 default :
34724                     break;
34725             }
34726             
34727             Roo.each(box, function(b,kk){
34728                 
34729                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34730                 
34731                 var sz = b.el.getSize();
34732                 
34733                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34734                 
34735             }, this);
34736             
34737         }, this);
34738         
34739         var mY = 0;
34740         
34741         for (var i = 0; i < this.cols; i++){
34742             mY = Math.max(mY, maxY[i]);
34743         }
34744         
34745         this.el.setHeight(mY - pos.y);
34746         
34747     },
34748     
34749 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34750 //    {
34751 //        var pos = this.el.getBox(true);
34752 //        var x = pos.x;
34753 //        var y = pos.y;
34754 //        var maxX = pos.right;
34755 //        
34756 //        var maxHeight = 0;
34757 //        
34758 //        Roo.each(items, function(item, k){
34759 //            
34760 //            var c = k % 2;
34761 //            
34762 //            item.el.position('absolute');
34763 //                
34764 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34765 //
34766 //            item.el.setWidth(width);
34767 //
34768 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34769 //
34770 //            item.el.setHeight(height);
34771 //            
34772 //            if(c == 0){
34773 //                item.el.setXY([x, y], isInstant ? false : true);
34774 //            } else {
34775 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34776 //            }
34777 //            
34778 //            y = y + height + this.alternativePadWidth;
34779 //            
34780 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34781 //            
34782 //        }, this);
34783 //        
34784 //        this.el.setHeight(maxHeight);
34785 //        
34786 //    },
34787     
34788     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34789     {
34790         var pos = this.el.getBox(true);
34791         
34792         var minX = pos.x;
34793         var minY = pos.y;
34794         
34795         var maxX = pos.right;
34796         
34797         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34798         
34799         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34800         
34801         Roo.each(queue, function(box, k){
34802             
34803             Roo.each(box, function(b, kk){
34804                 
34805                 b.el.position('absolute');
34806                 
34807                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34808                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34809                 
34810                 if(b.size == 'md-left' || b.size == 'md-right'){
34811                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34812                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34813                 }
34814                 
34815                 b.el.setWidth(width);
34816                 b.el.setHeight(height);
34817                 
34818             }, this);
34819             
34820             if(!box.length){
34821                 return;
34822             }
34823             
34824             var positions = [];
34825             
34826             switch (box.length){
34827                 case 1 :
34828                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34829                     break;
34830                 case 2 :
34831                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34832                     break;
34833                 case 3 :
34834                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34835                     break;
34836                 case 4 :
34837                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34838                     break;
34839                 default :
34840                     break;
34841             }
34842             
34843             Roo.each(box, function(b,kk){
34844                 
34845                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34846                 
34847                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34848                 
34849             }, this);
34850             
34851         }, this);
34852         
34853     },
34854     
34855     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34856     {
34857         Roo.each(eItems, function(b,k){
34858             
34859             b.size = (k == 0) ? 'sm' : 'xs';
34860             b.x = (k == 0) ? 2 : 1;
34861             b.y = (k == 0) ? 2 : 1;
34862             
34863             b.el.position('absolute');
34864             
34865             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34866                 
34867             b.el.setWidth(width);
34868             
34869             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34870             
34871             b.el.setHeight(height);
34872             
34873         }, this);
34874
34875         var positions = [];
34876         
34877         positions.push({
34878             x : maxX - this.unitWidth * 2 - this.gutter,
34879             y : minY
34880         });
34881         
34882         positions.push({
34883             x : maxX - this.unitWidth,
34884             y : minY + (this.unitWidth + this.gutter) * 2
34885         });
34886         
34887         positions.push({
34888             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34889             y : minY
34890         });
34891         
34892         Roo.each(eItems, function(b,k){
34893             
34894             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34895
34896         }, this);
34897         
34898     },
34899     
34900     getVerticalOneBoxColPositions : function(x, y, box)
34901     {
34902         var pos = [];
34903         
34904         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34905         
34906         if(box[0].size == 'md-left'){
34907             rand = 0;
34908         }
34909         
34910         if(box[0].size == 'md-right'){
34911             rand = 1;
34912         }
34913         
34914         pos.push({
34915             x : x + (this.unitWidth + this.gutter) * rand,
34916             y : y
34917         });
34918         
34919         return pos;
34920     },
34921     
34922     getVerticalTwoBoxColPositions : function(x, y, box)
34923     {
34924         var pos = [];
34925         
34926         if(box[0].size == 'xs'){
34927             
34928             pos.push({
34929                 x : x,
34930                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34931             });
34932
34933             pos.push({
34934                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34935                 y : y
34936             });
34937             
34938             return pos;
34939             
34940         }
34941         
34942         pos.push({
34943             x : x,
34944             y : y
34945         });
34946
34947         pos.push({
34948             x : x + (this.unitWidth + this.gutter) * 2,
34949             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34950         });
34951         
34952         return pos;
34953         
34954     },
34955     
34956     getVerticalThreeBoxColPositions : function(x, y, box)
34957     {
34958         var pos = [];
34959         
34960         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34961             
34962             pos.push({
34963                 x : x,
34964                 y : y
34965             });
34966
34967             pos.push({
34968                 x : x + (this.unitWidth + this.gutter) * 1,
34969                 y : y
34970             });
34971             
34972             pos.push({
34973                 x : x + (this.unitWidth + this.gutter) * 2,
34974                 y : y
34975             });
34976             
34977             return pos;
34978             
34979         }
34980         
34981         if(box[0].size == 'xs' && box[1].size == 'xs'){
34982             
34983             pos.push({
34984                 x : x,
34985                 y : y
34986             });
34987
34988             pos.push({
34989                 x : x,
34990                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34991             });
34992             
34993             pos.push({
34994                 x : x + (this.unitWidth + this.gutter) * 1,
34995                 y : y
34996             });
34997             
34998             return pos;
34999             
35000         }
35001         
35002         pos.push({
35003             x : x,
35004             y : y
35005         });
35006
35007         pos.push({
35008             x : x + (this.unitWidth + this.gutter) * 2,
35009             y : y
35010         });
35011
35012         pos.push({
35013             x : x + (this.unitWidth + this.gutter) * 2,
35014             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35015         });
35016             
35017         return pos;
35018         
35019     },
35020     
35021     getVerticalFourBoxColPositions : function(x, y, box)
35022     {
35023         var pos = [];
35024         
35025         if(box[0].size == 'xs'){
35026             
35027             pos.push({
35028                 x : x,
35029                 y : y
35030             });
35031
35032             pos.push({
35033                 x : x,
35034                 y : y + (this.unitHeight + this.gutter) * 1
35035             });
35036             
35037             pos.push({
35038                 x : x,
35039                 y : y + (this.unitHeight + this.gutter) * 2
35040             });
35041             
35042             pos.push({
35043                 x : x + (this.unitWidth + this.gutter) * 1,
35044                 y : y
35045             });
35046             
35047             return pos;
35048             
35049         }
35050         
35051         pos.push({
35052             x : x,
35053             y : y
35054         });
35055
35056         pos.push({
35057             x : x + (this.unitWidth + this.gutter) * 2,
35058             y : y
35059         });
35060
35061         pos.push({
35062             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35063             y : y + (this.unitHeight + this.gutter) * 1
35064         });
35065
35066         pos.push({
35067             x : x + (this.unitWidth + this.gutter) * 2,
35068             y : y + (this.unitWidth + this.gutter) * 2
35069         });
35070
35071         return pos;
35072         
35073     },
35074     
35075     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35076     {
35077         var pos = [];
35078         
35079         if(box[0].size == 'md-left'){
35080             pos.push({
35081                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35082                 y : minY
35083             });
35084             
35085             return pos;
35086         }
35087         
35088         if(box[0].size == 'md-right'){
35089             pos.push({
35090                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35091                 y : minY + (this.unitWidth + this.gutter) * 1
35092             });
35093             
35094             return pos;
35095         }
35096         
35097         var rand = Math.floor(Math.random() * (4 - box[0].y));
35098         
35099         pos.push({
35100             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35101             y : minY + (this.unitWidth + this.gutter) * rand
35102         });
35103         
35104         return pos;
35105         
35106     },
35107     
35108     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35109     {
35110         var pos = [];
35111         
35112         if(box[0].size == 'xs'){
35113             
35114             pos.push({
35115                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35116                 y : minY
35117             });
35118
35119             pos.push({
35120                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35121                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35122             });
35123             
35124             return pos;
35125             
35126         }
35127         
35128         pos.push({
35129             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35130             y : minY
35131         });
35132
35133         pos.push({
35134             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35135             y : minY + (this.unitWidth + this.gutter) * 2
35136         });
35137         
35138         return pos;
35139         
35140     },
35141     
35142     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35143     {
35144         var pos = [];
35145         
35146         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35147             
35148             pos.push({
35149                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35150                 y : minY
35151             });
35152
35153             pos.push({
35154                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35155                 y : minY + (this.unitWidth + this.gutter) * 1
35156             });
35157             
35158             pos.push({
35159                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35160                 y : minY + (this.unitWidth + this.gutter) * 2
35161             });
35162             
35163             return pos;
35164             
35165         }
35166         
35167         if(box[0].size == 'xs' && box[1].size == 'xs'){
35168             
35169             pos.push({
35170                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35171                 y : minY
35172             });
35173
35174             pos.push({
35175                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35176                 y : minY
35177             });
35178             
35179             pos.push({
35180                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35181                 y : minY + (this.unitWidth + this.gutter) * 1
35182             });
35183             
35184             return pos;
35185             
35186         }
35187         
35188         pos.push({
35189             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35190             y : minY
35191         });
35192
35193         pos.push({
35194             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35195             y : minY + (this.unitWidth + this.gutter) * 2
35196         });
35197
35198         pos.push({
35199             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35200             y : minY + (this.unitWidth + this.gutter) * 2
35201         });
35202             
35203         return pos;
35204         
35205     },
35206     
35207     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35208     {
35209         var pos = [];
35210         
35211         if(box[0].size == 'xs'){
35212             
35213             pos.push({
35214                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35215                 y : minY
35216             });
35217
35218             pos.push({
35219                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35220                 y : minY
35221             });
35222             
35223             pos.push({
35224                 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),
35225                 y : minY
35226             });
35227             
35228             pos.push({
35229                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35230                 y : minY + (this.unitWidth + this.gutter) * 1
35231             });
35232             
35233             return pos;
35234             
35235         }
35236         
35237         pos.push({
35238             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35239             y : minY
35240         });
35241         
35242         pos.push({
35243             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35244             y : minY + (this.unitWidth + this.gutter) * 2
35245         });
35246         
35247         pos.push({
35248             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35249             y : minY + (this.unitWidth + this.gutter) * 2
35250         });
35251         
35252         pos.push({
35253             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),
35254             y : minY + (this.unitWidth + this.gutter) * 2
35255         });
35256
35257         return pos;
35258         
35259     },
35260     
35261     /**
35262     * remove a Masonry Brick
35263     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35264     */
35265     removeBrick : function(brick_id)
35266     {
35267         if (!brick_id) {
35268             return;
35269         }
35270         
35271         for (var i = 0; i<this.bricks.length; i++) {
35272             if (this.bricks[i].id == brick_id) {
35273                 this.bricks.splice(i,1);
35274                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35275                 this.initial();
35276             }
35277         }
35278     },
35279     
35280     /**
35281     * adds a Masonry Brick
35282     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35283     */
35284     addBrick : function(cfg)
35285     {
35286         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35287         //this.register(cn);
35288         cn.parentId = this.id;
35289         cn.render(this.el);
35290         return cn;
35291     },
35292     
35293     /**
35294     * register a Masonry Brick
35295     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35296     */
35297     
35298     register : function(brick)
35299     {
35300         this.bricks.push(brick);
35301         brick.masonryId = this.id;
35302     },
35303     
35304     /**
35305     * clear all the Masonry Brick
35306     */
35307     clearAll : function()
35308     {
35309         this.bricks = [];
35310         //this.getChildContainer().dom.innerHTML = "";
35311         this.el.dom.innerHTML = '';
35312     },
35313     
35314     getSelected : function()
35315     {
35316         if (!this.selectedBrick) {
35317             return false;
35318         }
35319         
35320         return this.selectedBrick;
35321     }
35322 });
35323
35324 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35325     
35326     groups: {},
35327      /**
35328     * register a Masonry Layout
35329     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35330     */
35331     
35332     register : function(layout)
35333     {
35334         this.groups[layout.id] = layout;
35335     },
35336     /**
35337     * fetch a  Masonry Layout based on the masonry layout ID
35338     * @param {string} the masonry layout to add
35339     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35340     */
35341     
35342     get: function(layout_id) {
35343         if (typeof(this.groups[layout_id]) == 'undefined') {
35344             return false;
35345         }
35346         return this.groups[layout_id] ;
35347     }
35348     
35349     
35350     
35351 });
35352
35353  
35354
35355  /**
35356  *
35357  * This is based on 
35358  * http://masonry.desandro.com
35359  *
35360  * The idea is to render all the bricks based on vertical width...
35361  *
35362  * The original code extends 'outlayer' - we might need to use that....
35363  * 
35364  */
35365
35366
35367 /**
35368  * @class Roo.bootstrap.LayoutMasonryAuto
35369  * @extends Roo.bootstrap.Component
35370  * Bootstrap Layout Masonry class
35371  * 
35372  * @constructor
35373  * Create a new Element
35374  * @param {Object} config The config object
35375  */
35376
35377 Roo.bootstrap.LayoutMasonryAuto = function(config){
35378     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35379 };
35380
35381 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35382     
35383       /**
35384      * @cfg {Boolean} isFitWidth  - resize the width..
35385      */   
35386     isFitWidth : false,  // options..
35387     /**
35388      * @cfg {Boolean} isOriginLeft = left align?
35389      */   
35390     isOriginLeft : true,
35391     /**
35392      * @cfg {Boolean} isOriginTop = top align?
35393      */   
35394     isOriginTop : false,
35395     /**
35396      * @cfg {Boolean} isLayoutInstant = no animation?
35397      */   
35398     isLayoutInstant : false, // needed?
35399     /**
35400      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35401      */   
35402     isResizingContainer : true,
35403     /**
35404      * @cfg {Number} columnWidth  width of the columns 
35405      */   
35406     
35407     columnWidth : 0,
35408     
35409     /**
35410      * @cfg {Number} maxCols maximum number of columns
35411      */   
35412     
35413     maxCols: 0,
35414     /**
35415      * @cfg {Number} padHeight padding below box..
35416      */   
35417     
35418     padHeight : 10, 
35419     
35420     /**
35421      * @cfg {Boolean} isAutoInitial defalut true
35422      */   
35423     
35424     isAutoInitial : true, 
35425     
35426     // private?
35427     gutter : 0,
35428     
35429     containerWidth: 0,
35430     initialColumnWidth : 0,
35431     currentSize : null,
35432     
35433     colYs : null, // array.
35434     maxY : 0,
35435     padWidth: 10,
35436     
35437     
35438     tag: 'div',
35439     cls: '',
35440     bricks: null, //CompositeElement
35441     cols : 0, // array?
35442     // element : null, // wrapped now this.el
35443     _isLayoutInited : null, 
35444     
35445     
35446     getAutoCreate : function(){
35447         
35448         var cfg = {
35449             tag: this.tag,
35450             cls: 'blog-masonary-wrapper ' + this.cls,
35451             cn : {
35452                 cls : 'mas-boxes masonary'
35453             }
35454         };
35455         
35456         return cfg;
35457     },
35458     
35459     getChildContainer: function( )
35460     {
35461         if (this.boxesEl) {
35462             return this.boxesEl;
35463         }
35464         
35465         this.boxesEl = this.el.select('.mas-boxes').first();
35466         
35467         return this.boxesEl;
35468     },
35469     
35470     
35471     initEvents : function()
35472     {
35473         var _this = this;
35474         
35475         if(this.isAutoInitial){
35476             Roo.log('hook children rendered');
35477             this.on('childrenrendered', function() {
35478                 Roo.log('children rendered');
35479                 _this.initial();
35480             } ,this);
35481         }
35482         
35483     },
35484     
35485     initial : function()
35486     {
35487         this.reloadItems();
35488
35489         this.currentSize = this.el.getBox(true);
35490
35491         /// was window resize... - let's see if this works..
35492         Roo.EventManager.onWindowResize(this.resize, this); 
35493
35494         if(!this.isAutoInitial){
35495             this.layout();
35496             return;
35497         }
35498         
35499         this.layout.defer(500,this);
35500     },
35501     
35502     reloadItems: function()
35503     {
35504         this.bricks = this.el.select('.masonry-brick', true);
35505         
35506         this.bricks.each(function(b) {
35507             //Roo.log(b.getSize());
35508             if (!b.attr('originalwidth')) {
35509                 b.attr('originalwidth',  b.getSize().width);
35510             }
35511             
35512         });
35513         
35514         Roo.log(this.bricks.elements.length);
35515     },
35516     
35517     resize : function()
35518     {
35519         Roo.log('resize');
35520         var cs = this.el.getBox(true);
35521         
35522         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35523             Roo.log("no change in with or X");
35524             return;
35525         }
35526         this.currentSize = cs;
35527         this.layout();
35528     },
35529     
35530     layout : function()
35531     {
35532          Roo.log('layout');
35533         this._resetLayout();
35534         //this._manageStamps();
35535       
35536         // don't animate first layout
35537         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35538         this.layoutItems( isInstant );
35539       
35540         // flag for initalized
35541         this._isLayoutInited = true;
35542     },
35543     
35544     layoutItems : function( isInstant )
35545     {
35546         //var items = this._getItemsForLayout( this.items );
35547         // original code supports filtering layout items.. we just ignore it..
35548         
35549         this._layoutItems( this.bricks , isInstant );
35550       
35551         this._postLayout();
35552     },
35553     _layoutItems : function ( items , isInstant)
35554     {
35555        //this.fireEvent( 'layout', this, items );
35556     
35557
35558         if ( !items || !items.elements.length ) {
35559           // no items, emit event with empty array
35560             return;
35561         }
35562
35563         var queue = [];
35564         items.each(function(item) {
35565             Roo.log("layout item");
35566             Roo.log(item);
35567             // get x/y object from method
35568             var position = this._getItemLayoutPosition( item );
35569             // enqueue
35570             position.item = item;
35571             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35572             queue.push( position );
35573         }, this);
35574       
35575         this._processLayoutQueue( queue );
35576     },
35577     /** Sets position of item in DOM
35578     * @param {Element} item
35579     * @param {Number} x - horizontal position
35580     * @param {Number} y - vertical position
35581     * @param {Boolean} isInstant - disables transitions
35582     */
35583     _processLayoutQueue : function( queue )
35584     {
35585         for ( var i=0, len = queue.length; i < len; i++ ) {
35586             var obj = queue[i];
35587             obj.item.position('absolute');
35588             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35589         }
35590     },
35591       
35592     
35593     /**
35594     * Any logic you want to do after each layout,
35595     * i.e. size the container
35596     */
35597     _postLayout : function()
35598     {
35599         this.resizeContainer();
35600     },
35601     
35602     resizeContainer : function()
35603     {
35604         if ( !this.isResizingContainer ) {
35605             return;
35606         }
35607         var size = this._getContainerSize();
35608         if ( size ) {
35609             this.el.setSize(size.width,size.height);
35610             this.boxesEl.setSize(size.width,size.height);
35611         }
35612     },
35613     
35614     
35615     
35616     _resetLayout : function()
35617     {
35618         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35619         this.colWidth = this.el.getWidth();
35620         //this.gutter = this.el.getWidth(); 
35621         
35622         this.measureColumns();
35623
35624         // reset column Y
35625         var i = this.cols;
35626         this.colYs = [];
35627         while (i--) {
35628             this.colYs.push( 0 );
35629         }
35630     
35631         this.maxY = 0;
35632     },
35633
35634     measureColumns : function()
35635     {
35636         this.getContainerWidth();
35637       // if columnWidth is 0, default to outerWidth of first item
35638         if ( !this.columnWidth ) {
35639             var firstItem = this.bricks.first();
35640             Roo.log(firstItem);
35641             this.columnWidth  = this.containerWidth;
35642             if (firstItem && firstItem.attr('originalwidth') ) {
35643                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35644             }
35645             // columnWidth fall back to item of first element
35646             Roo.log("set column width?");
35647                         this.initialColumnWidth = this.columnWidth  ;
35648
35649             // if first elem has no width, default to size of container
35650             
35651         }
35652         
35653         
35654         if (this.initialColumnWidth) {
35655             this.columnWidth = this.initialColumnWidth;
35656         }
35657         
35658         
35659             
35660         // column width is fixed at the top - however if container width get's smaller we should
35661         // reduce it...
35662         
35663         // this bit calcs how man columns..
35664             
35665         var columnWidth = this.columnWidth += this.gutter;
35666       
35667         // calculate columns
35668         var containerWidth = this.containerWidth + this.gutter;
35669         
35670         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35671         // fix rounding errors, typically with gutters
35672         var excess = columnWidth - containerWidth % columnWidth;
35673         
35674         
35675         // if overshoot is less than a pixel, round up, otherwise floor it
35676         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35677         cols = Math[ mathMethod ]( cols );
35678         this.cols = Math.max( cols, 1 );
35679         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35680         
35681          // padding positioning..
35682         var totalColWidth = this.cols * this.columnWidth;
35683         var padavail = this.containerWidth - totalColWidth;
35684         // so for 2 columns - we need 3 'pads'
35685         
35686         var padNeeded = (1+this.cols) * this.padWidth;
35687         
35688         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35689         
35690         this.columnWidth += padExtra
35691         //this.padWidth = Math.floor(padavail /  ( this.cols));
35692         
35693         // adjust colum width so that padding is fixed??
35694         
35695         // we have 3 columns ... total = width * 3
35696         // we have X left over... that should be used by 
35697         
35698         //if (this.expandC) {
35699             
35700         //}
35701         
35702         
35703         
35704     },
35705     
35706     getContainerWidth : function()
35707     {
35708        /* // container is parent if fit width
35709         var container = this.isFitWidth ? this.element.parentNode : this.element;
35710         // check that this.size and size are there
35711         // IE8 triggers resize on body size change, so they might not be
35712         
35713         var size = getSize( container );  //FIXME
35714         this.containerWidth = size && size.innerWidth; //FIXME
35715         */
35716          
35717         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35718         
35719     },
35720     
35721     _getItemLayoutPosition : function( item )  // what is item?
35722     {
35723         // we resize the item to our columnWidth..
35724       
35725         item.setWidth(this.columnWidth);
35726         item.autoBoxAdjust  = false;
35727         
35728         var sz = item.getSize();
35729  
35730         // how many columns does this brick span
35731         var remainder = this.containerWidth % this.columnWidth;
35732         
35733         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35734         // round if off by 1 pixel, otherwise use ceil
35735         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35736         colSpan = Math.min( colSpan, this.cols );
35737         
35738         // normally this should be '1' as we dont' currently allow multi width columns..
35739         
35740         var colGroup = this._getColGroup( colSpan );
35741         // get the minimum Y value from the columns
35742         var minimumY = Math.min.apply( Math, colGroup );
35743         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35744         
35745         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35746          
35747         // position the brick
35748         var position = {
35749             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35750             y: this.currentSize.y + minimumY + this.padHeight
35751         };
35752         
35753         Roo.log(position);
35754         // apply setHeight to necessary columns
35755         var setHeight = minimumY + sz.height + this.padHeight;
35756         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35757         
35758         var setSpan = this.cols + 1 - colGroup.length;
35759         for ( var i = 0; i < setSpan; i++ ) {
35760           this.colYs[ shortColIndex + i ] = setHeight ;
35761         }
35762       
35763         return position;
35764     },
35765     
35766     /**
35767      * @param {Number} colSpan - number of columns the element spans
35768      * @returns {Array} colGroup
35769      */
35770     _getColGroup : function( colSpan )
35771     {
35772         if ( colSpan < 2 ) {
35773           // if brick spans only one column, use all the column Ys
35774           return this.colYs;
35775         }
35776       
35777         var colGroup = [];
35778         // how many different places could this brick fit horizontally
35779         var groupCount = this.cols + 1 - colSpan;
35780         // for each group potential horizontal position
35781         for ( var i = 0; i < groupCount; i++ ) {
35782           // make an array of colY values for that one group
35783           var groupColYs = this.colYs.slice( i, i + colSpan );
35784           // and get the max value of the array
35785           colGroup[i] = Math.max.apply( Math, groupColYs );
35786         }
35787         return colGroup;
35788     },
35789     /*
35790     _manageStamp : function( stamp )
35791     {
35792         var stampSize =  stamp.getSize();
35793         var offset = stamp.getBox();
35794         // get the columns that this stamp affects
35795         var firstX = this.isOriginLeft ? offset.x : offset.right;
35796         var lastX = firstX + stampSize.width;
35797         var firstCol = Math.floor( firstX / this.columnWidth );
35798         firstCol = Math.max( 0, firstCol );
35799         
35800         var lastCol = Math.floor( lastX / this.columnWidth );
35801         // lastCol should not go over if multiple of columnWidth #425
35802         lastCol -= lastX % this.columnWidth ? 0 : 1;
35803         lastCol = Math.min( this.cols - 1, lastCol );
35804         
35805         // set colYs to bottom of the stamp
35806         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35807             stampSize.height;
35808             
35809         for ( var i = firstCol; i <= lastCol; i++ ) {
35810           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35811         }
35812     },
35813     */
35814     
35815     _getContainerSize : function()
35816     {
35817         this.maxY = Math.max.apply( Math, this.colYs );
35818         var size = {
35819             height: this.maxY
35820         };
35821       
35822         if ( this.isFitWidth ) {
35823             size.width = this._getContainerFitWidth();
35824         }
35825       
35826         return size;
35827     },
35828     
35829     _getContainerFitWidth : function()
35830     {
35831         var unusedCols = 0;
35832         // count unused columns
35833         var i = this.cols;
35834         while ( --i ) {
35835           if ( this.colYs[i] !== 0 ) {
35836             break;
35837           }
35838           unusedCols++;
35839         }
35840         // fit container to columns that have been used
35841         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35842     },
35843     
35844     needsResizeLayout : function()
35845     {
35846         var previousWidth = this.containerWidth;
35847         this.getContainerWidth();
35848         return previousWidth !== this.containerWidth;
35849     }
35850  
35851 });
35852
35853  
35854
35855  /*
35856  * - LGPL
35857  *
35858  * element
35859  * 
35860  */
35861
35862 /**
35863  * @class Roo.bootstrap.MasonryBrick
35864  * @extends Roo.bootstrap.Component
35865  * Bootstrap MasonryBrick class
35866  * 
35867  * @constructor
35868  * Create a new MasonryBrick
35869  * @param {Object} config The config object
35870  */
35871
35872 Roo.bootstrap.MasonryBrick = function(config){
35873     
35874     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35875     
35876     Roo.bootstrap.MasonryBrick.register(this);
35877     
35878     this.addEvents({
35879         // raw events
35880         /**
35881          * @event click
35882          * When a MasonryBrick is clcik
35883          * @param {Roo.bootstrap.MasonryBrick} this
35884          * @param {Roo.EventObject} e
35885          */
35886         "click" : true
35887     });
35888 };
35889
35890 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35891     
35892     /**
35893      * @cfg {String} title
35894      */   
35895     title : '',
35896     /**
35897      * @cfg {String} html
35898      */   
35899     html : '',
35900     /**
35901      * @cfg {String} bgimage
35902      */   
35903     bgimage : '',
35904     /**
35905      * @cfg {String} videourl
35906      */   
35907     videourl : '',
35908     /**
35909      * @cfg {String} cls
35910      */   
35911     cls : '',
35912     /**
35913      * @cfg {String} href
35914      */   
35915     href : '',
35916     /**
35917      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35918      */   
35919     size : 'xs',
35920     
35921     /**
35922      * @cfg {String} placetitle (center|bottom)
35923      */   
35924     placetitle : '',
35925     
35926     /**
35927      * @cfg {Boolean} isFitContainer defalut true
35928      */   
35929     isFitContainer : true, 
35930     
35931     /**
35932      * @cfg {Boolean} preventDefault defalut false
35933      */   
35934     preventDefault : false, 
35935     
35936     /**
35937      * @cfg {Boolean} inverse defalut false
35938      */   
35939     maskInverse : false, 
35940     
35941     getAutoCreate : function()
35942     {
35943         if(!this.isFitContainer){
35944             return this.getSplitAutoCreate();
35945         }
35946         
35947         var cls = 'masonry-brick masonry-brick-full';
35948         
35949         if(this.href.length){
35950             cls += ' masonry-brick-link';
35951         }
35952         
35953         if(this.bgimage.length){
35954             cls += ' masonry-brick-image';
35955         }
35956         
35957         if(this.maskInverse){
35958             cls += ' mask-inverse';
35959         }
35960         
35961         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35962             cls += ' enable-mask';
35963         }
35964         
35965         if(this.size){
35966             cls += ' masonry-' + this.size + '-brick';
35967         }
35968         
35969         if(this.placetitle.length){
35970             
35971             switch (this.placetitle) {
35972                 case 'center' :
35973                     cls += ' masonry-center-title';
35974                     break;
35975                 case 'bottom' :
35976                     cls += ' masonry-bottom-title';
35977                     break;
35978                 default:
35979                     break;
35980             }
35981             
35982         } else {
35983             if(!this.html.length && !this.bgimage.length){
35984                 cls += ' masonry-center-title';
35985             }
35986
35987             if(!this.html.length && this.bgimage.length){
35988                 cls += ' masonry-bottom-title';
35989             }
35990         }
35991         
35992         if(this.cls){
35993             cls += ' ' + this.cls;
35994         }
35995         
35996         var cfg = {
35997             tag: (this.href.length) ? 'a' : 'div',
35998             cls: cls,
35999             cn: [
36000                 {
36001                     tag: 'div',
36002                     cls: 'masonry-brick-mask'
36003                 },
36004                 {
36005                     tag: 'div',
36006                     cls: 'masonry-brick-paragraph',
36007                     cn: []
36008                 }
36009             ]
36010         };
36011         
36012         if(this.href.length){
36013             cfg.href = this.href;
36014         }
36015         
36016         var cn = cfg.cn[1].cn;
36017         
36018         if(this.title.length){
36019             cn.push({
36020                 tag: 'h4',
36021                 cls: 'masonry-brick-title',
36022                 html: this.title
36023             });
36024         }
36025         
36026         if(this.html.length){
36027             cn.push({
36028                 tag: 'p',
36029                 cls: 'masonry-brick-text',
36030                 html: this.html
36031             });
36032         }
36033         
36034         if (!this.title.length && !this.html.length) {
36035             cfg.cn[1].cls += ' hide';
36036         }
36037         
36038         if(this.bgimage.length){
36039             cfg.cn.push({
36040                 tag: 'img',
36041                 cls: 'masonry-brick-image-view',
36042                 src: this.bgimage
36043             });
36044         }
36045         
36046         if(this.videourl.length){
36047             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36048             // youtube support only?
36049             cfg.cn.push({
36050                 tag: 'iframe',
36051                 cls: 'masonry-brick-image-view',
36052                 src: vurl,
36053                 frameborder : 0,
36054                 allowfullscreen : true
36055             });
36056         }
36057         
36058         return cfg;
36059         
36060     },
36061     
36062     getSplitAutoCreate : function()
36063     {
36064         var cls = 'masonry-brick masonry-brick-split';
36065         
36066         if(this.href.length){
36067             cls += ' masonry-brick-link';
36068         }
36069         
36070         if(this.bgimage.length){
36071             cls += ' masonry-brick-image';
36072         }
36073         
36074         if(this.size){
36075             cls += ' masonry-' + this.size + '-brick';
36076         }
36077         
36078         switch (this.placetitle) {
36079             case 'center' :
36080                 cls += ' masonry-center-title';
36081                 break;
36082             case 'bottom' :
36083                 cls += ' masonry-bottom-title';
36084                 break;
36085             default:
36086                 if(!this.bgimage.length){
36087                     cls += ' masonry-center-title';
36088                 }
36089
36090                 if(this.bgimage.length){
36091                     cls += ' masonry-bottom-title';
36092                 }
36093                 break;
36094         }
36095         
36096         if(this.cls){
36097             cls += ' ' + this.cls;
36098         }
36099         
36100         var cfg = {
36101             tag: (this.href.length) ? 'a' : 'div',
36102             cls: cls,
36103             cn: [
36104                 {
36105                     tag: 'div',
36106                     cls: 'masonry-brick-split-head',
36107                     cn: [
36108                         {
36109                             tag: 'div',
36110                             cls: 'masonry-brick-paragraph',
36111                             cn: []
36112                         }
36113                     ]
36114                 },
36115                 {
36116                     tag: 'div',
36117                     cls: 'masonry-brick-split-body',
36118                     cn: []
36119                 }
36120             ]
36121         };
36122         
36123         if(this.href.length){
36124             cfg.href = this.href;
36125         }
36126         
36127         if(this.title.length){
36128             cfg.cn[0].cn[0].cn.push({
36129                 tag: 'h4',
36130                 cls: 'masonry-brick-title',
36131                 html: this.title
36132             });
36133         }
36134         
36135         if(this.html.length){
36136             cfg.cn[1].cn.push({
36137                 tag: 'p',
36138                 cls: 'masonry-brick-text',
36139                 html: this.html
36140             });
36141         }
36142
36143         if(this.bgimage.length){
36144             cfg.cn[0].cn.push({
36145                 tag: 'img',
36146                 cls: 'masonry-brick-image-view',
36147                 src: this.bgimage
36148             });
36149         }
36150         
36151         if(this.videourl.length){
36152             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36153             // youtube support only?
36154             cfg.cn[0].cn.cn.push({
36155                 tag: 'iframe',
36156                 cls: 'masonry-brick-image-view',
36157                 src: vurl,
36158                 frameborder : 0,
36159                 allowfullscreen : true
36160             });
36161         }
36162         
36163         return cfg;
36164     },
36165     
36166     initEvents: function() 
36167     {
36168         switch (this.size) {
36169             case 'xs' :
36170                 this.x = 1;
36171                 this.y = 1;
36172                 break;
36173             case 'sm' :
36174                 this.x = 2;
36175                 this.y = 2;
36176                 break;
36177             case 'md' :
36178             case 'md-left' :
36179             case 'md-right' :
36180                 this.x = 3;
36181                 this.y = 3;
36182                 break;
36183             case 'tall' :
36184                 this.x = 2;
36185                 this.y = 3;
36186                 break;
36187             case 'wide' :
36188                 this.x = 3;
36189                 this.y = 2;
36190                 break;
36191             case 'wide-thin' :
36192                 this.x = 3;
36193                 this.y = 1;
36194                 break;
36195                         
36196             default :
36197                 break;
36198         }
36199         
36200         if(Roo.isTouch){
36201             this.el.on('touchstart', this.onTouchStart, this);
36202             this.el.on('touchmove', this.onTouchMove, this);
36203             this.el.on('touchend', this.onTouchEnd, this);
36204             this.el.on('contextmenu', this.onContextMenu, this);
36205         } else {
36206             this.el.on('mouseenter'  ,this.enter, this);
36207             this.el.on('mouseleave', this.leave, this);
36208             this.el.on('click', this.onClick, this);
36209         }
36210         
36211         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36212             this.parent().bricks.push(this);   
36213         }
36214         
36215     },
36216     
36217     onClick: function(e, el)
36218     {
36219         var time = this.endTimer - this.startTimer;
36220         // Roo.log(e.preventDefault());
36221         if(Roo.isTouch){
36222             if(time > 1000){
36223                 e.preventDefault();
36224                 return;
36225             }
36226         }
36227         
36228         if(!this.preventDefault){
36229             return;
36230         }
36231         
36232         e.preventDefault();
36233         
36234         if (this.activeClass != '') {
36235             this.selectBrick();
36236         }
36237         
36238         this.fireEvent('click', this, e);
36239     },
36240     
36241     enter: function(e, el)
36242     {
36243         e.preventDefault();
36244         
36245         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36246             return;
36247         }
36248         
36249         if(this.bgimage.length && this.html.length){
36250             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36251         }
36252     },
36253     
36254     leave: function(e, el)
36255     {
36256         e.preventDefault();
36257         
36258         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36259             return;
36260         }
36261         
36262         if(this.bgimage.length && this.html.length){
36263             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36264         }
36265     },
36266     
36267     onTouchStart: function(e, el)
36268     {
36269 //        e.preventDefault();
36270         
36271         this.touchmoved = false;
36272         
36273         if(!this.isFitContainer){
36274             return;
36275         }
36276         
36277         if(!this.bgimage.length || !this.html.length){
36278             return;
36279         }
36280         
36281         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36282         
36283         this.timer = new Date().getTime();
36284         
36285     },
36286     
36287     onTouchMove: function(e, el)
36288     {
36289         this.touchmoved = true;
36290     },
36291     
36292     onContextMenu : function(e,el)
36293     {
36294         e.preventDefault();
36295         e.stopPropagation();
36296         return false;
36297     },
36298     
36299     onTouchEnd: function(e, el)
36300     {
36301 //        e.preventDefault();
36302         
36303         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36304         
36305             this.leave(e,el);
36306             
36307             return;
36308         }
36309         
36310         if(!this.bgimage.length || !this.html.length){
36311             
36312             if(this.href.length){
36313                 window.location.href = this.href;
36314             }
36315             
36316             return;
36317         }
36318         
36319         if(!this.isFitContainer){
36320             return;
36321         }
36322         
36323         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36324         
36325         window.location.href = this.href;
36326     },
36327     
36328     //selection on single brick only
36329     selectBrick : function() {
36330         
36331         if (!this.parentId) {
36332             return;
36333         }
36334         
36335         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36336         var index = m.selectedBrick.indexOf(this.id);
36337         
36338         if ( index > -1) {
36339             m.selectedBrick.splice(index,1);
36340             this.el.removeClass(this.activeClass);
36341             return;
36342         }
36343         
36344         for(var i = 0; i < m.selectedBrick.length; i++) {
36345             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36346             b.el.removeClass(b.activeClass);
36347         }
36348         
36349         m.selectedBrick = [];
36350         
36351         m.selectedBrick.push(this.id);
36352         this.el.addClass(this.activeClass);
36353         return;
36354     },
36355     
36356     isSelected : function(){
36357         return this.el.hasClass(this.activeClass);
36358         
36359     }
36360 });
36361
36362 Roo.apply(Roo.bootstrap.MasonryBrick, {
36363     
36364     //groups: {},
36365     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36366      /**
36367     * register a Masonry Brick
36368     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36369     */
36370     
36371     register : function(brick)
36372     {
36373         //this.groups[brick.id] = brick;
36374         this.groups.add(brick.id, brick);
36375     },
36376     /**
36377     * fetch a  masonry brick based on the masonry brick ID
36378     * @param {string} the masonry brick to add
36379     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36380     */
36381     
36382     get: function(brick_id) 
36383     {
36384         // if (typeof(this.groups[brick_id]) == 'undefined') {
36385         //     return false;
36386         // }
36387         // return this.groups[brick_id] ;
36388         
36389         if(this.groups.key(brick_id)) {
36390             return this.groups.key(brick_id);
36391         }
36392         
36393         return false;
36394     }
36395     
36396     
36397     
36398 });
36399
36400  /*
36401  * - LGPL
36402  *
36403  * element
36404  * 
36405  */
36406
36407 /**
36408  * @class Roo.bootstrap.Brick
36409  * @extends Roo.bootstrap.Component
36410  * Bootstrap Brick class
36411  * 
36412  * @constructor
36413  * Create a new Brick
36414  * @param {Object} config The config object
36415  */
36416
36417 Roo.bootstrap.Brick = function(config){
36418     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36419     
36420     this.addEvents({
36421         // raw events
36422         /**
36423          * @event click
36424          * When a Brick is click
36425          * @param {Roo.bootstrap.Brick} this
36426          * @param {Roo.EventObject} e
36427          */
36428         "click" : true
36429     });
36430 };
36431
36432 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36433     
36434     /**
36435      * @cfg {String} title
36436      */   
36437     title : '',
36438     /**
36439      * @cfg {String} html
36440      */   
36441     html : '',
36442     /**
36443      * @cfg {String} bgimage
36444      */   
36445     bgimage : '',
36446     /**
36447      * @cfg {String} cls
36448      */   
36449     cls : '',
36450     /**
36451      * @cfg {String} href
36452      */   
36453     href : '',
36454     /**
36455      * @cfg {String} video
36456      */   
36457     video : '',
36458     /**
36459      * @cfg {Boolean} square
36460      */   
36461     square : true,
36462     
36463     getAutoCreate : function()
36464     {
36465         var cls = 'roo-brick';
36466         
36467         if(this.href.length){
36468             cls += ' roo-brick-link';
36469         }
36470         
36471         if(this.bgimage.length){
36472             cls += ' roo-brick-image';
36473         }
36474         
36475         if(!this.html.length && !this.bgimage.length){
36476             cls += ' roo-brick-center-title';
36477         }
36478         
36479         if(!this.html.length && this.bgimage.length){
36480             cls += ' roo-brick-bottom-title';
36481         }
36482         
36483         if(this.cls){
36484             cls += ' ' + this.cls;
36485         }
36486         
36487         var cfg = {
36488             tag: (this.href.length) ? 'a' : 'div',
36489             cls: cls,
36490             cn: [
36491                 {
36492                     tag: 'div',
36493                     cls: 'roo-brick-paragraph',
36494                     cn: []
36495                 }
36496             ]
36497         };
36498         
36499         if(this.href.length){
36500             cfg.href = this.href;
36501         }
36502         
36503         var cn = cfg.cn[0].cn;
36504         
36505         if(this.title.length){
36506             cn.push({
36507                 tag: 'h4',
36508                 cls: 'roo-brick-title',
36509                 html: this.title
36510             });
36511         }
36512         
36513         if(this.html.length){
36514             cn.push({
36515                 tag: 'p',
36516                 cls: 'roo-brick-text',
36517                 html: this.html
36518             });
36519         } else {
36520             cn.cls += ' hide';
36521         }
36522         
36523         if(this.bgimage.length){
36524             cfg.cn.push({
36525                 tag: 'img',
36526                 cls: 'roo-brick-image-view',
36527                 src: this.bgimage
36528             });
36529         }
36530         
36531         return cfg;
36532     },
36533     
36534     initEvents: function() 
36535     {
36536         if(this.title.length || this.html.length){
36537             this.el.on('mouseenter'  ,this.enter, this);
36538             this.el.on('mouseleave', this.leave, this);
36539         }
36540         
36541         Roo.EventManager.onWindowResize(this.resize, this); 
36542         
36543         if(this.bgimage.length){
36544             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36545             this.imageEl.on('load', this.onImageLoad, this);
36546             return;
36547         }
36548         
36549         this.resize();
36550     },
36551     
36552     onImageLoad : function()
36553     {
36554         this.resize();
36555     },
36556     
36557     resize : function()
36558     {
36559         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36560         
36561         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36562         
36563         if(this.bgimage.length){
36564             var image = this.el.select('.roo-brick-image-view', true).first();
36565             
36566             image.setWidth(paragraph.getWidth());
36567             
36568             if(this.square){
36569                 image.setHeight(paragraph.getWidth());
36570             }
36571             
36572             this.el.setHeight(image.getHeight());
36573             paragraph.setHeight(image.getHeight());
36574             
36575         }
36576         
36577     },
36578     
36579     enter: function(e, el)
36580     {
36581         e.preventDefault();
36582         
36583         if(this.bgimage.length){
36584             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36585             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36586         }
36587     },
36588     
36589     leave: function(e, el)
36590     {
36591         e.preventDefault();
36592         
36593         if(this.bgimage.length){
36594             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36595             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36596         }
36597     }
36598     
36599 });
36600
36601  
36602
36603  /*
36604  * - LGPL
36605  *
36606  * Number field 
36607  */
36608
36609 /**
36610  * @class Roo.bootstrap.NumberField
36611  * @extends Roo.bootstrap.Input
36612  * Bootstrap NumberField class
36613  * 
36614  * 
36615  * 
36616  * 
36617  * @constructor
36618  * Create a new NumberField
36619  * @param {Object} config The config object
36620  */
36621
36622 Roo.bootstrap.NumberField = function(config){
36623     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36624 };
36625
36626 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36627     
36628     /**
36629      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36630      */
36631     allowDecimals : true,
36632     /**
36633      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36634      */
36635     decimalSeparator : ".",
36636     /**
36637      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36638      */
36639     decimalPrecision : 2,
36640     /**
36641      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36642      */
36643     allowNegative : true,
36644     
36645     /**
36646      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36647      */
36648     allowZero: true,
36649     /**
36650      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36651      */
36652     minValue : Number.NEGATIVE_INFINITY,
36653     /**
36654      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36655      */
36656     maxValue : Number.MAX_VALUE,
36657     /**
36658      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36659      */
36660     minText : "The minimum value for this field is {0}",
36661     /**
36662      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36663      */
36664     maxText : "The maximum value for this field is {0}",
36665     /**
36666      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36667      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36668      */
36669     nanText : "{0} is not a valid number",
36670     /**
36671      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36672      */
36673     thousandsDelimiter : false,
36674     /**
36675      * @cfg {String} valueAlign alignment of value
36676      */
36677     valueAlign : "left",
36678
36679     getAutoCreate : function()
36680     {
36681         var hiddenInput = {
36682             tag: 'input',
36683             type: 'hidden',
36684             id: Roo.id(),
36685             cls: 'hidden-number-input'
36686         };
36687         
36688         if (this.name) {
36689             hiddenInput.name = this.name;
36690         }
36691         
36692         this.name = '';
36693         
36694         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36695         
36696         this.name = hiddenInput.name;
36697         
36698         if(cfg.cn.length > 0) {
36699             cfg.cn.push(hiddenInput);
36700         }
36701         
36702         return cfg;
36703     },
36704
36705     // private
36706     initEvents : function()
36707     {   
36708         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36709         
36710         var allowed = "0123456789";
36711         
36712         if(this.allowDecimals){
36713             allowed += this.decimalSeparator;
36714         }
36715         
36716         if(this.allowNegative){
36717             allowed += "-";
36718         }
36719         
36720         if(this.thousandsDelimiter) {
36721             allowed += ",";
36722         }
36723         
36724         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36725         
36726         var keyPress = function(e){
36727             
36728             var k = e.getKey();
36729             
36730             var c = e.getCharCode();
36731             
36732             if(
36733                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36734                     allowed.indexOf(String.fromCharCode(c)) === -1
36735             ){
36736                 e.stopEvent();
36737                 return;
36738             }
36739             
36740             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36741                 return;
36742             }
36743             
36744             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36745                 e.stopEvent();
36746             }
36747         };
36748         
36749         this.el.on("keypress", keyPress, this);
36750     },
36751     
36752     validateValue : function(value)
36753     {
36754         
36755         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36756             return false;
36757         }
36758         
36759         var num = this.parseValue(value);
36760         
36761         if(isNaN(num)){
36762             this.markInvalid(String.format(this.nanText, value));
36763             return false;
36764         }
36765         
36766         if(num < this.minValue){
36767             this.markInvalid(String.format(this.minText, this.minValue));
36768             return false;
36769         }
36770         
36771         if(num > this.maxValue){
36772             this.markInvalid(String.format(this.maxText, this.maxValue));
36773             return false;
36774         }
36775         
36776         return true;
36777     },
36778
36779     getValue : function()
36780     {
36781         var v = this.hiddenEl().getValue();
36782         
36783         return this.fixPrecision(this.parseValue(v));
36784     },
36785
36786     parseValue : function(value)
36787     {
36788         if(this.thousandsDelimiter) {
36789             value += "";
36790             r = new RegExp(",", "g");
36791             value = value.replace(r, "");
36792         }
36793         
36794         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36795         return isNaN(value) ? '' : value;
36796     },
36797
36798     fixPrecision : function(value)
36799     {
36800         if(this.thousandsDelimiter) {
36801             value += "";
36802             r = new RegExp(",", "g");
36803             value = value.replace(r, "");
36804         }
36805         
36806         var nan = isNaN(value);
36807         
36808         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36809             return nan ? '' : value;
36810         }
36811         return parseFloat(value).toFixed(this.decimalPrecision);
36812     },
36813
36814     setValue : function(v)
36815     {
36816         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36817         
36818         this.value = v;
36819         
36820         if(this.rendered){
36821             
36822             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36823             
36824             this.inputEl().dom.value = (v == '') ? '' :
36825                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36826             
36827             if(!this.allowZero && v === '0') {
36828                 this.hiddenEl().dom.value = '';
36829                 this.inputEl().dom.value = '';
36830             }
36831             
36832             this.validate();
36833         }
36834     },
36835
36836     decimalPrecisionFcn : function(v)
36837     {
36838         return Math.floor(v);
36839     },
36840
36841     beforeBlur : function()
36842     {
36843         var v = this.parseValue(this.getRawValue());
36844         
36845         if(v || v === 0 || v === ''){
36846             this.setValue(v);
36847         }
36848     },
36849     
36850     hiddenEl : function()
36851     {
36852         return this.el.select('input.hidden-number-input',true).first();
36853     }
36854     
36855 });
36856
36857  
36858
36859 /*
36860 * Licence: LGPL
36861 */
36862
36863 /**
36864  * @class Roo.bootstrap.DocumentSlider
36865  * @extends Roo.bootstrap.Component
36866  * Bootstrap DocumentSlider class
36867  * 
36868  * @constructor
36869  * Create a new DocumentViewer
36870  * @param {Object} config The config object
36871  */
36872
36873 Roo.bootstrap.DocumentSlider = function(config){
36874     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36875     
36876     this.files = [];
36877     
36878     this.addEvents({
36879         /**
36880          * @event initial
36881          * Fire after initEvent
36882          * @param {Roo.bootstrap.DocumentSlider} this
36883          */
36884         "initial" : true,
36885         /**
36886          * @event update
36887          * Fire after update
36888          * @param {Roo.bootstrap.DocumentSlider} this
36889          */
36890         "update" : true,
36891         /**
36892          * @event click
36893          * Fire after click
36894          * @param {Roo.bootstrap.DocumentSlider} this
36895          */
36896         "click" : true
36897     });
36898 };
36899
36900 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36901     
36902     files : false,
36903     
36904     indicator : 0,
36905     
36906     getAutoCreate : function()
36907     {
36908         var cfg = {
36909             tag : 'div',
36910             cls : 'roo-document-slider',
36911             cn : [
36912                 {
36913                     tag : 'div',
36914                     cls : 'roo-document-slider-header',
36915                     cn : [
36916                         {
36917                             tag : 'div',
36918                             cls : 'roo-document-slider-header-title'
36919                         }
36920                     ]
36921                 },
36922                 {
36923                     tag : 'div',
36924                     cls : 'roo-document-slider-body',
36925                     cn : [
36926                         {
36927                             tag : 'div',
36928                             cls : 'roo-document-slider-prev',
36929                             cn : [
36930                                 {
36931                                     tag : 'i',
36932                                     cls : 'fa fa-chevron-left'
36933                                 }
36934                             ]
36935                         },
36936                         {
36937                             tag : 'div',
36938                             cls : 'roo-document-slider-thumb',
36939                             cn : [
36940                                 {
36941                                     tag : 'img',
36942                                     cls : 'roo-document-slider-image'
36943                                 }
36944                             ]
36945                         },
36946                         {
36947                             tag : 'div',
36948                             cls : 'roo-document-slider-next',
36949                             cn : [
36950                                 {
36951                                     tag : 'i',
36952                                     cls : 'fa fa-chevron-right'
36953                                 }
36954                             ]
36955                         }
36956                     ]
36957                 }
36958             ]
36959         };
36960         
36961         return cfg;
36962     },
36963     
36964     initEvents : function()
36965     {
36966         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36967         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36968         
36969         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36970         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36971         
36972         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36973         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36974         
36975         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36976         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36977         
36978         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36979         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36980         
36981         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36982         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36983         
36984         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36985         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36986         
36987         this.thumbEl.on('click', this.onClick, this);
36988         
36989         this.prevIndicator.on('click', this.prev, this);
36990         
36991         this.nextIndicator.on('click', this.next, this);
36992         
36993     },
36994     
36995     initial : function()
36996     {
36997         if(this.files.length){
36998             this.indicator = 1;
36999             this.update()
37000         }
37001         
37002         this.fireEvent('initial', this);
37003     },
37004     
37005     update : function()
37006     {
37007         this.imageEl.attr('src', this.files[this.indicator - 1]);
37008         
37009         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37010         
37011         this.prevIndicator.show();
37012         
37013         if(this.indicator == 1){
37014             this.prevIndicator.hide();
37015         }
37016         
37017         this.nextIndicator.show();
37018         
37019         if(this.indicator == this.files.length){
37020             this.nextIndicator.hide();
37021         }
37022         
37023         this.thumbEl.scrollTo('top');
37024         
37025         this.fireEvent('update', this);
37026     },
37027     
37028     onClick : function(e)
37029     {
37030         e.preventDefault();
37031         
37032         this.fireEvent('click', this);
37033     },
37034     
37035     prev : function(e)
37036     {
37037         e.preventDefault();
37038         
37039         this.indicator = Math.max(1, this.indicator - 1);
37040         
37041         this.update();
37042     },
37043     
37044     next : function(e)
37045     {
37046         e.preventDefault();
37047         
37048         this.indicator = Math.min(this.files.length, this.indicator + 1);
37049         
37050         this.update();
37051     }
37052 });
37053 /*
37054  * - LGPL
37055  *
37056  * RadioSet
37057  *
37058  *
37059  */
37060
37061 /**
37062  * @class Roo.bootstrap.RadioSet
37063  * @extends Roo.bootstrap.Input
37064  * Bootstrap RadioSet class
37065  * @cfg {String} indicatorpos (left|right) default left
37066  * @cfg {Boolean} inline (true|false) inline the element (default true)
37067  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37068  * @constructor
37069  * Create a new RadioSet
37070  * @param {Object} config The config object
37071  */
37072
37073 Roo.bootstrap.RadioSet = function(config){
37074     
37075     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37076     
37077     this.radioes = [];
37078     
37079     Roo.bootstrap.RadioSet.register(this);
37080     
37081     this.addEvents({
37082         /**
37083         * @event check
37084         * Fires when the element is checked or unchecked.
37085         * @param {Roo.bootstrap.RadioSet} this This radio
37086         * @param {Roo.bootstrap.Radio} item The checked item
37087         */
37088        check : true,
37089        /**
37090         * @event click
37091         * Fires when the element is click.
37092         * @param {Roo.bootstrap.RadioSet} this This radio set
37093         * @param {Roo.bootstrap.Radio} item The checked item
37094         * @param {Roo.EventObject} e The event object
37095         */
37096        click : true
37097     });
37098     
37099 };
37100
37101 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37102
37103     radioes : false,
37104     
37105     inline : true,
37106     
37107     weight : '',
37108     
37109     indicatorpos : 'left',
37110     
37111     getAutoCreate : function()
37112     {
37113         var label = {
37114             tag : 'label',
37115             cls : 'roo-radio-set-label',
37116             cn : [
37117                 {
37118                     tag : 'span',
37119                     html : this.fieldLabel
37120                 }
37121             ]
37122         };
37123         if (Roo.bootstrap.version == 3) {
37124             
37125             
37126             if(this.indicatorpos == 'left'){
37127                 label.cn.unshift({
37128                     tag : 'i',
37129                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37130                     tooltip : 'This field is required'
37131                 });
37132             } else {
37133                 label.cn.push({
37134                     tag : 'i',
37135                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37136                     tooltip : 'This field is required'
37137                 });
37138             }
37139         }
37140         var items = {
37141             tag : 'div',
37142             cls : 'roo-radio-set-items'
37143         };
37144         
37145         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37146         
37147         if (align === 'left' && this.fieldLabel.length) {
37148             
37149             items = {
37150                 cls : "roo-radio-set-right", 
37151                 cn: [
37152                     items
37153                 ]
37154             };
37155             
37156             if(this.labelWidth > 12){
37157                 label.style = "width: " + this.labelWidth + 'px';
37158             }
37159             
37160             if(this.labelWidth < 13 && this.labelmd == 0){
37161                 this.labelmd = this.labelWidth;
37162             }
37163             
37164             if(this.labellg > 0){
37165                 label.cls += ' col-lg-' + this.labellg;
37166                 items.cls += ' col-lg-' + (12 - this.labellg);
37167             }
37168             
37169             if(this.labelmd > 0){
37170                 label.cls += ' col-md-' + this.labelmd;
37171                 items.cls += ' col-md-' + (12 - this.labelmd);
37172             }
37173             
37174             if(this.labelsm > 0){
37175                 label.cls += ' col-sm-' + this.labelsm;
37176                 items.cls += ' col-sm-' + (12 - this.labelsm);
37177             }
37178             
37179             if(this.labelxs > 0){
37180                 label.cls += ' col-xs-' + this.labelxs;
37181                 items.cls += ' col-xs-' + (12 - this.labelxs);
37182             }
37183         }
37184         
37185         var cfg = {
37186             tag : 'div',
37187             cls : 'roo-radio-set',
37188             cn : [
37189                 {
37190                     tag : 'input',
37191                     cls : 'roo-radio-set-input',
37192                     type : 'hidden',
37193                     name : this.name,
37194                     value : this.value ? this.value :  ''
37195                 },
37196                 label,
37197                 items
37198             ]
37199         };
37200         
37201         if(this.weight.length){
37202             cfg.cls += ' roo-radio-' + this.weight;
37203         }
37204         
37205         if(this.inline) {
37206             cfg.cls += ' roo-radio-set-inline';
37207         }
37208         
37209         var settings=this;
37210         ['xs','sm','md','lg'].map(function(size){
37211             if (settings[size]) {
37212                 cfg.cls += ' col-' + size + '-' + settings[size];
37213             }
37214         });
37215         
37216         return cfg;
37217         
37218     },
37219
37220     initEvents : function()
37221     {
37222         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37223         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37224         
37225         if(!this.fieldLabel.length){
37226             this.labelEl.hide();
37227         }
37228         
37229         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37230         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37231         
37232         this.indicator = this.indicatorEl();
37233         
37234         if(this.indicator){
37235             this.indicator.addClass('invisible');
37236         }
37237         
37238         this.originalValue = this.getValue();
37239         
37240     },
37241     
37242     inputEl: function ()
37243     {
37244         return this.el.select('.roo-radio-set-input', true).first();
37245     },
37246     
37247     getChildContainer : function()
37248     {
37249         return this.itemsEl;
37250     },
37251     
37252     register : function(item)
37253     {
37254         this.radioes.push(item);
37255         
37256     },
37257     
37258     validate : function()
37259     {   
37260         if(this.getVisibilityEl().hasClass('hidden')){
37261             return true;
37262         }
37263         
37264         var valid = false;
37265         
37266         Roo.each(this.radioes, function(i){
37267             if(!i.checked){
37268                 return;
37269             }
37270             
37271             valid = true;
37272             return false;
37273         });
37274         
37275         if(this.allowBlank) {
37276             return true;
37277         }
37278         
37279         if(this.disabled || valid){
37280             this.markValid();
37281             return true;
37282         }
37283         
37284         this.markInvalid();
37285         return false;
37286         
37287     },
37288     
37289     markValid : function()
37290     {
37291         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37292             this.indicatorEl().removeClass('visible');
37293             this.indicatorEl().addClass('invisible');
37294         }
37295         
37296         
37297         if (Roo.bootstrap.version == 3) {
37298             this.el.removeClass([this.invalidClass, this.validClass]);
37299             this.el.addClass(this.validClass);
37300         } else {
37301             this.el.removeClass(['is-invalid','is-valid']);
37302             this.el.addClass(['is-valid']);
37303         }
37304         this.fireEvent('valid', this);
37305     },
37306     
37307     markInvalid : function(msg)
37308     {
37309         if(this.allowBlank || this.disabled){
37310             return;
37311         }
37312         
37313         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37314             this.indicatorEl().removeClass('invisible');
37315             this.indicatorEl().addClass('visible');
37316         }
37317         if (Roo.bootstrap.version == 3) {
37318             this.el.removeClass([this.invalidClass, this.validClass]);
37319             this.el.addClass(this.invalidClass);
37320         } else {
37321             this.el.removeClass(['is-invalid','is-valid']);
37322             this.el.addClass(['is-invalid']);
37323         }
37324         
37325         this.fireEvent('invalid', this, msg);
37326         
37327     },
37328     
37329     setValue : function(v, suppressEvent)
37330     {   
37331         if(this.value === v){
37332             return;
37333         }
37334         
37335         this.value = v;
37336         
37337         if(this.rendered){
37338             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37339         }
37340         
37341         Roo.each(this.radioes, function(i){
37342             i.checked = false;
37343             i.el.removeClass('checked');
37344         });
37345         
37346         Roo.each(this.radioes, function(i){
37347             
37348             if(i.value === v || i.value.toString() === v.toString()){
37349                 i.checked = true;
37350                 i.el.addClass('checked');
37351                 
37352                 if(suppressEvent !== true){
37353                     this.fireEvent('check', this, i);
37354                 }
37355                 
37356                 return false;
37357             }
37358             
37359         }, this);
37360         
37361         this.validate();
37362     },
37363     
37364     clearInvalid : function(){
37365         
37366         if(!this.el || this.preventMark){
37367             return;
37368         }
37369         
37370         this.el.removeClass([this.invalidClass]);
37371         
37372         this.fireEvent('valid', this);
37373     }
37374     
37375 });
37376
37377 Roo.apply(Roo.bootstrap.RadioSet, {
37378     
37379     groups: {},
37380     
37381     register : function(set)
37382     {
37383         this.groups[set.name] = set;
37384     },
37385     
37386     get: function(name) 
37387     {
37388         if (typeof(this.groups[name]) == 'undefined') {
37389             return false;
37390         }
37391         
37392         return this.groups[name] ;
37393     }
37394     
37395 });
37396 /*
37397  * Based on:
37398  * Ext JS Library 1.1.1
37399  * Copyright(c) 2006-2007, Ext JS, LLC.
37400  *
37401  * Originally Released Under LGPL - original licence link has changed is not relivant.
37402  *
37403  * Fork - LGPL
37404  * <script type="text/javascript">
37405  */
37406
37407
37408 /**
37409  * @class Roo.bootstrap.SplitBar
37410  * @extends Roo.util.Observable
37411  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37412  * <br><br>
37413  * Usage:
37414  * <pre><code>
37415 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37416                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37417 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37418 split.minSize = 100;
37419 split.maxSize = 600;
37420 split.animate = true;
37421 split.on('moved', splitterMoved);
37422 </code></pre>
37423  * @constructor
37424  * Create a new SplitBar
37425  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37426  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37427  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37428  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37429                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37430                         position of the SplitBar).
37431  */
37432 Roo.bootstrap.SplitBar = function(cfg){
37433     
37434     /** @private */
37435     
37436     //{
37437     //  dragElement : elm
37438     //  resizingElement: el,
37439         // optional..
37440     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37441     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37442         // existingProxy ???
37443     //}
37444     
37445     this.el = Roo.get(cfg.dragElement, true);
37446     this.el.dom.unselectable = "on";
37447     /** @private */
37448     this.resizingEl = Roo.get(cfg.resizingElement, true);
37449
37450     /**
37451      * @private
37452      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37453      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37454      * @type Number
37455      */
37456     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37457     
37458     /**
37459      * The minimum size of the resizing element. (Defaults to 0)
37460      * @type Number
37461      */
37462     this.minSize = 0;
37463     
37464     /**
37465      * The maximum size of the resizing element. (Defaults to 2000)
37466      * @type Number
37467      */
37468     this.maxSize = 2000;
37469     
37470     /**
37471      * Whether to animate the transition to the new size
37472      * @type Boolean
37473      */
37474     this.animate = false;
37475     
37476     /**
37477      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37478      * @type Boolean
37479      */
37480     this.useShim = false;
37481     
37482     /** @private */
37483     this.shim = null;
37484     
37485     if(!cfg.existingProxy){
37486         /** @private */
37487         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37488     }else{
37489         this.proxy = Roo.get(cfg.existingProxy).dom;
37490     }
37491     /** @private */
37492     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37493     
37494     /** @private */
37495     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37496     
37497     /** @private */
37498     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37499     
37500     /** @private */
37501     this.dragSpecs = {};
37502     
37503     /**
37504      * @private The adapter to use to positon and resize elements
37505      */
37506     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37507     this.adapter.init(this);
37508     
37509     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37510         /** @private */
37511         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37512         this.el.addClass("roo-splitbar-h");
37513     }else{
37514         /** @private */
37515         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37516         this.el.addClass("roo-splitbar-v");
37517     }
37518     
37519     this.addEvents({
37520         /**
37521          * @event resize
37522          * Fires when the splitter is moved (alias for {@link #event-moved})
37523          * @param {Roo.bootstrap.SplitBar} this
37524          * @param {Number} newSize the new width or height
37525          */
37526         "resize" : true,
37527         /**
37528          * @event moved
37529          * Fires when the splitter is moved
37530          * @param {Roo.bootstrap.SplitBar} this
37531          * @param {Number} newSize the new width or height
37532          */
37533         "moved" : true,
37534         /**
37535          * @event beforeresize
37536          * Fires before the splitter is dragged
37537          * @param {Roo.bootstrap.SplitBar} this
37538          */
37539         "beforeresize" : true,
37540
37541         "beforeapply" : true
37542     });
37543
37544     Roo.util.Observable.call(this);
37545 };
37546
37547 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37548     onStartProxyDrag : function(x, y){
37549         this.fireEvent("beforeresize", this);
37550         if(!this.overlay){
37551             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37552             o.unselectable();
37553             o.enableDisplayMode("block");
37554             // all splitbars share the same overlay
37555             Roo.bootstrap.SplitBar.prototype.overlay = o;
37556         }
37557         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37558         this.overlay.show();
37559         Roo.get(this.proxy).setDisplayed("block");
37560         var size = this.adapter.getElementSize(this);
37561         this.activeMinSize = this.getMinimumSize();;
37562         this.activeMaxSize = this.getMaximumSize();;
37563         var c1 = size - this.activeMinSize;
37564         var c2 = Math.max(this.activeMaxSize - size, 0);
37565         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37566             this.dd.resetConstraints();
37567             this.dd.setXConstraint(
37568                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37569                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37570             );
37571             this.dd.setYConstraint(0, 0);
37572         }else{
37573             this.dd.resetConstraints();
37574             this.dd.setXConstraint(0, 0);
37575             this.dd.setYConstraint(
37576                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37577                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37578             );
37579          }
37580         this.dragSpecs.startSize = size;
37581         this.dragSpecs.startPoint = [x, y];
37582         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37583     },
37584     
37585     /** 
37586      * @private Called after the drag operation by the DDProxy
37587      */
37588     onEndProxyDrag : function(e){
37589         Roo.get(this.proxy).setDisplayed(false);
37590         var endPoint = Roo.lib.Event.getXY(e);
37591         if(this.overlay){
37592             this.overlay.hide();
37593         }
37594         var newSize;
37595         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37596             newSize = this.dragSpecs.startSize + 
37597                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37598                     endPoint[0] - this.dragSpecs.startPoint[0] :
37599                     this.dragSpecs.startPoint[0] - endPoint[0]
37600                 );
37601         }else{
37602             newSize = this.dragSpecs.startSize + 
37603                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37604                     endPoint[1] - this.dragSpecs.startPoint[1] :
37605                     this.dragSpecs.startPoint[1] - endPoint[1]
37606                 );
37607         }
37608         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37609         if(newSize != this.dragSpecs.startSize){
37610             if(this.fireEvent('beforeapply', this, newSize) !== false){
37611                 this.adapter.setElementSize(this, newSize);
37612                 this.fireEvent("moved", this, newSize);
37613                 this.fireEvent("resize", this, newSize);
37614             }
37615         }
37616     },
37617     
37618     /**
37619      * Get the adapter this SplitBar uses
37620      * @return The adapter object
37621      */
37622     getAdapter : function(){
37623         return this.adapter;
37624     },
37625     
37626     /**
37627      * Set the adapter this SplitBar uses
37628      * @param {Object} adapter A SplitBar adapter object
37629      */
37630     setAdapter : function(adapter){
37631         this.adapter = adapter;
37632         this.adapter.init(this);
37633     },
37634     
37635     /**
37636      * Gets the minimum size for the resizing element
37637      * @return {Number} The minimum size
37638      */
37639     getMinimumSize : function(){
37640         return this.minSize;
37641     },
37642     
37643     /**
37644      * Sets the minimum size for the resizing element
37645      * @param {Number} minSize The minimum size
37646      */
37647     setMinimumSize : function(minSize){
37648         this.minSize = minSize;
37649     },
37650     
37651     /**
37652      * Gets the maximum size for the resizing element
37653      * @return {Number} The maximum size
37654      */
37655     getMaximumSize : function(){
37656         return this.maxSize;
37657     },
37658     
37659     /**
37660      * Sets the maximum size for the resizing element
37661      * @param {Number} maxSize The maximum size
37662      */
37663     setMaximumSize : function(maxSize){
37664         this.maxSize = maxSize;
37665     },
37666     
37667     /**
37668      * Sets the initialize size for the resizing element
37669      * @param {Number} size The initial size
37670      */
37671     setCurrentSize : function(size){
37672         var oldAnimate = this.animate;
37673         this.animate = false;
37674         this.adapter.setElementSize(this, size);
37675         this.animate = oldAnimate;
37676     },
37677     
37678     /**
37679      * Destroy this splitbar. 
37680      * @param {Boolean} removeEl True to remove the element
37681      */
37682     destroy : function(removeEl){
37683         if(this.shim){
37684             this.shim.remove();
37685         }
37686         this.dd.unreg();
37687         this.proxy.parentNode.removeChild(this.proxy);
37688         if(removeEl){
37689             this.el.remove();
37690         }
37691     }
37692 });
37693
37694 /**
37695  * @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.
37696  */
37697 Roo.bootstrap.SplitBar.createProxy = function(dir){
37698     var proxy = new Roo.Element(document.createElement("div"));
37699     proxy.unselectable();
37700     var cls = 'roo-splitbar-proxy';
37701     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37702     document.body.appendChild(proxy.dom);
37703     return proxy.dom;
37704 };
37705
37706 /** 
37707  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37708  * Default Adapter. It assumes the splitter and resizing element are not positioned
37709  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37710  */
37711 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37712 };
37713
37714 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37715     // do nothing for now
37716     init : function(s){
37717     
37718     },
37719     /**
37720      * Called before drag operations to get the current size of the resizing element. 
37721      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37722      */
37723      getElementSize : function(s){
37724         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37725             return s.resizingEl.getWidth();
37726         }else{
37727             return s.resizingEl.getHeight();
37728         }
37729     },
37730     
37731     /**
37732      * Called after drag operations to set the size of the resizing element.
37733      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37734      * @param {Number} newSize The new size to set
37735      * @param {Function} onComplete A function to be invoked when resizing is complete
37736      */
37737     setElementSize : function(s, newSize, onComplete){
37738         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37739             if(!s.animate){
37740                 s.resizingEl.setWidth(newSize);
37741                 if(onComplete){
37742                     onComplete(s, newSize);
37743                 }
37744             }else{
37745                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37746             }
37747         }else{
37748             
37749             if(!s.animate){
37750                 s.resizingEl.setHeight(newSize);
37751                 if(onComplete){
37752                     onComplete(s, newSize);
37753                 }
37754             }else{
37755                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37756             }
37757         }
37758     }
37759 };
37760
37761 /** 
37762  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37763  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37764  * Adapter that  moves the splitter element to align with the resized sizing element. 
37765  * Used with an absolute positioned SplitBar.
37766  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37767  * document.body, make sure you assign an id to the body element.
37768  */
37769 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37770     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37771     this.container = Roo.get(container);
37772 };
37773
37774 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37775     init : function(s){
37776         this.basic.init(s);
37777     },
37778     
37779     getElementSize : function(s){
37780         return this.basic.getElementSize(s);
37781     },
37782     
37783     setElementSize : function(s, newSize, onComplete){
37784         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37785     },
37786     
37787     moveSplitter : function(s){
37788         var yes = Roo.bootstrap.SplitBar;
37789         switch(s.placement){
37790             case yes.LEFT:
37791                 s.el.setX(s.resizingEl.getRight());
37792                 break;
37793             case yes.RIGHT:
37794                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37795                 break;
37796             case yes.TOP:
37797                 s.el.setY(s.resizingEl.getBottom());
37798                 break;
37799             case yes.BOTTOM:
37800                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37801                 break;
37802         }
37803     }
37804 };
37805
37806 /**
37807  * Orientation constant - Create a vertical SplitBar
37808  * @static
37809  * @type Number
37810  */
37811 Roo.bootstrap.SplitBar.VERTICAL = 1;
37812
37813 /**
37814  * Orientation constant - Create a horizontal SplitBar
37815  * @static
37816  * @type Number
37817  */
37818 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37819
37820 /**
37821  * Placement constant - The resizing element is to the left of the splitter element
37822  * @static
37823  * @type Number
37824  */
37825 Roo.bootstrap.SplitBar.LEFT = 1;
37826
37827 /**
37828  * Placement constant - The resizing element is to the right of the splitter element
37829  * @static
37830  * @type Number
37831  */
37832 Roo.bootstrap.SplitBar.RIGHT = 2;
37833
37834 /**
37835  * Placement constant - The resizing element is positioned above the splitter element
37836  * @static
37837  * @type Number
37838  */
37839 Roo.bootstrap.SplitBar.TOP = 3;
37840
37841 /**
37842  * Placement constant - The resizing element is positioned under splitter element
37843  * @static
37844  * @type Number
37845  */
37846 Roo.bootstrap.SplitBar.BOTTOM = 4;
37847 Roo.namespace("Roo.bootstrap.layout");/*
37848  * Based on:
37849  * Ext JS Library 1.1.1
37850  * Copyright(c) 2006-2007, Ext JS, LLC.
37851  *
37852  * Originally Released Under LGPL - original licence link has changed is not relivant.
37853  *
37854  * Fork - LGPL
37855  * <script type="text/javascript">
37856  */
37857
37858 /**
37859  * @class Roo.bootstrap.layout.Manager
37860  * @extends Roo.bootstrap.Component
37861  * Base class for layout managers.
37862  */
37863 Roo.bootstrap.layout.Manager = function(config)
37864 {
37865     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37866
37867
37868
37869
37870
37871     /** false to disable window resize monitoring @type Boolean */
37872     this.monitorWindowResize = true;
37873     this.regions = {};
37874     this.addEvents({
37875         /**
37876          * @event layout
37877          * Fires when a layout is performed.
37878          * @param {Roo.LayoutManager} this
37879          */
37880         "layout" : true,
37881         /**
37882          * @event regionresized
37883          * Fires when the user resizes a region.
37884          * @param {Roo.LayoutRegion} region The resized region
37885          * @param {Number} newSize The new size (width for east/west, height for north/south)
37886          */
37887         "regionresized" : true,
37888         /**
37889          * @event regioncollapsed
37890          * Fires when a region is collapsed.
37891          * @param {Roo.LayoutRegion} region The collapsed region
37892          */
37893         "regioncollapsed" : true,
37894         /**
37895          * @event regionexpanded
37896          * Fires when a region is expanded.
37897          * @param {Roo.LayoutRegion} region The expanded region
37898          */
37899         "regionexpanded" : true
37900     });
37901     this.updating = false;
37902
37903     if (config.el) {
37904         this.el = Roo.get(config.el);
37905         this.initEvents();
37906     }
37907
37908 };
37909
37910 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37911
37912
37913     regions : null,
37914
37915     monitorWindowResize : true,
37916
37917
37918     updating : false,
37919
37920
37921     onRender : function(ct, position)
37922     {
37923         if(!this.el){
37924             this.el = Roo.get(ct);
37925             this.initEvents();
37926         }
37927         //this.fireEvent('render',this);
37928     },
37929
37930
37931     initEvents: function()
37932     {
37933
37934
37935         // ie scrollbar fix
37936         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37937             document.body.scroll = "no";
37938         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37939             this.el.position('relative');
37940         }
37941         this.id = this.el.id;
37942         this.el.addClass("roo-layout-container");
37943         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37944         if(this.el.dom != document.body ) {
37945             this.el.on('resize', this.layout,this);
37946             this.el.on('show', this.layout,this);
37947         }
37948
37949     },
37950
37951     /**
37952      * Returns true if this layout is currently being updated
37953      * @return {Boolean}
37954      */
37955     isUpdating : function(){
37956         return this.updating;
37957     },
37958
37959     /**
37960      * Suspend the LayoutManager from doing auto-layouts while
37961      * making multiple add or remove calls
37962      */
37963     beginUpdate : function(){
37964         this.updating = true;
37965     },
37966
37967     /**
37968      * Restore auto-layouts and optionally disable the manager from performing a layout
37969      * @param {Boolean} noLayout true to disable a layout update
37970      */
37971     endUpdate : function(noLayout){
37972         this.updating = false;
37973         if(!noLayout){
37974             this.layout();
37975         }
37976     },
37977
37978     layout: function(){
37979         // abstract...
37980     },
37981
37982     onRegionResized : function(region, newSize){
37983         this.fireEvent("regionresized", region, newSize);
37984         this.layout();
37985     },
37986
37987     onRegionCollapsed : function(region){
37988         this.fireEvent("regioncollapsed", region);
37989     },
37990
37991     onRegionExpanded : function(region){
37992         this.fireEvent("regionexpanded", region);
37993     },
37994
37995     /**
37996      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37997      * performs box-model adjustments.
37998      * @return {Object} The size as an object {width: (the width), height: (the height)}
37999      */
38000     getViewSize : function()
38001     {
38002         var size;
38003         if(this.el.dom != document.body){
38004             size = this.el.getSize();
38005         }else{
38006             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38007         }
38008         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38009         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38010         return size;
38011     },
38012
38013     /**
38014      * Returns the Element this layout is bound to.
38015      * @return {Roo.Element}
38016      */
38017     getEl : function(){
38018         return this.el;
38019     },
38020
38021     /**
38022      * Returns the specified region.
38023      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38024      * @return {Roo.LayoutRegion}
38025      */
38026     getRegion : function(target){
38027         return this.regions[target.toLowerCase()];
38028     },
38029
38030     onWindowResize : function(){
38031         if(this.monitorWindowResize){
38032             this.layout();
38033         }
38034     }
38035 });
38036 /*
38037  * Based on:
38038  * Ext JS Library 1.1.1
38039  * Copyright(c) 2006-2007, Ext JS, LLC.
38040  *
38041  * Originally Released Under LGPL - original licence link has changed is not relivant.
38042  *
38043  * Fork - LGPL
38044  * <script type="text/javascript">
38045  */
38046 /**
38047  * @class Roo.bootstrap.layout.Border
38048  * @extends Roo.bootstrap.layout.Manager
38049  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38050  * please see: examples/bootstrap/nested.html<br><br>
38051  
38052 <b>The container the layout is rendered into can be either the body element or any other element.
38053 If it is not the body element, the container needs to either be an absolute positioned element,
38054 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38055 the container size if it is not the body element.</b>
38056
38057 * @constructor
38058 * Create a new Border
38059 * @param {Object} config Configuration options
38060  */
38061 Roo.bootstrap.layout.Border = function(config){
38062     config = config || {};
38063     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38064     
38065     
38066     
38067     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38068         if(config[region]){
38069             config[region].region = region;
38070             this.addRegion(config[region]);
38071         }
38072     },this);
38073     
38074 };
38075
38076 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38077
38078 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38079     
38080     parent : false, // this might point to a 'nest' or a ???
38081     
38082     /**
38083      * Creates and adds a new region if it doesn't already exist.
38084      * @param {String} target The target region key (north, south, east, west or center).
38085      * @param {Object} config The regions config object
38086      * @return {BorderLayoutRegion} The new region
38087      */
38088     addRegion : function(config)
38089     {
38090         if(!this.regions[config.region]){
38091             var r = this.factory(config);
38092             this.bindRegion(r);
38093         }
38094         return this.regions[config.region];
38095     },
38096
38097     // private (kinda)
38098     bindRegion : function(r){
38099         this.regions[r.config.region] = r;
38100         
38101         r.on("visibilitychange",    this.layout, this);
38102         r.on("paneladded",          this.layout, this);
38103         r.on("panelremoved",        this.layout, this);
38104         r.on("invalidated",         this.layout, this);
38105         r.on("resized",             this.onRegionResized, this);
38106         r.on("collapsed",           this.onRegionCollapsed, this);
38107         r.on("expanded",            this.onRegionExpanded, this);
38108     },
38109
38110     /**
38111      * Performs a layout update.
38112      */
38113     layout : function()
38114     {
38115         if(this.updating) {
38116             return;
38117         }
38118         
38119         // render all the rebions if they have not been done alreayd?
38120         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38121             if(this.regions[region] && !this.regions[region].bodyEl){
38122                 this.regions[region].onRender(this.el)
38123             }
38124         },this);
38125         
38126         var size = this.getViewSize();
38127         var w = size.width;
38128         var h = size.height;
38129         var centerW = w;
38130         var centerH = h;
38131         var centerY = 0;
38132         var centerX = 0;
38133         //var x = 0, y = 0;
38134
38135         var rs = this.regions;
38136         var north = rs["north"];
38137         var south = rs["south"]; 
38138         var west = rs["west"];
38139         var east = rs["east"];
38140         var center = rs["center"];
38141         //if(this.hideOnLayout){ // not supported anymore
38142             //c.el.setStyle("display", "none");
38143         //}
38144         if(north && north.isVisible()){
38145             var b = north.getBox();
38146             var m = north.getMargins();
38147             b.width = w - (m.left+m.right);
38148             b.x = m.left;
38149             b.y = m.top;
38150             centerY = b.height + b.y + m.bottom;
38151             centerH -= centerY;
38152             north.updateBox(this.safeBox(b));
38153         }
38154         if(south && south.isVisible()){
38155             var b = south.getBox();
38156             var m = south.getMargins();
38157             b.width = w - (m.left+m.right);
38158             b.x = m.left;
38159             var totalHeight = (b.height + m.top + m.bottom);
38160             b.y = h - totalHeight + m.top;
38161             centerH -= totalHeight;
38162             south.updateBox(this.safeBox(b));
38163         }
38164         if(west && west.isVisible()){
38165             var b = west.getBox();
38166             var m = west.getMargins();
38167             b.height = centerH - (m.top+m.bottom);
38168             b.x = m.left;
38169             b.y = centerY + m.top;
38170             var totalWidth = (b.width + m.left + m.right);
38171             centerX += totalWidth;
38172             centerW -= totalWidth;
38173             west.updateBox(this.safeBox(b));
38174         }
38175         if(east && east.isVisible()){
38176             var b = east.getBox();
38177             var m = east.getMargins();
38178             b.height = centerH - (m.top+m.bottom);
38179             var totalWidth = (b.width + m.left + m.right);
38180             b.x = w - totalWidth + m.left;
38181             b.y = centerY + m.top;
38182             centerW -= totalWidth;
38183             east.updateBox(this.safeBox(b));
38184         }
38185         if(center){
38186             var m = center.getMargins();
38187             var centerBox = {
38188                 x: centerX + m.left,
38189                 y: centerY + m.top,
38190                 width: centerW - (m.left+m.right),
38191                 height: centerH - (m.top+m.bottom)
38192             };
38193             //if(this.hideOnLayout){
38194                 //center.el.setStyle("display", "block");
38195             //}
38196             center.updateBox(this.safeBox(centerBox));
38197         }
38198         this.el.repaint();
38199         this.fireEvent("layout", this);
38200     },
38201
38202     // private
38203     safeBox : function(box){
38204         box.width = Math.max(0, box.width);
38205         box.height = Math.max(0, box.height);
38206         return box;
38207     },
38208
38209     /**
38210      * Adds a ContentPanel (or subclass) to this layout.
38211      * @param {String} target The target region key (north, south, east, west or center).
38212      * @param {Roo.ContentPanel} panel The panel to add
38213      * @return {Roo.ContentPanel} The added panel
38214      */
38215     add : function(target, panel){
38216          
38217         target = target.toLowerCase();
38218         return this.regions[target].add(panel);
38219     },
38220
38221     /**
38222      * Remove a ContentPanel (or subclass) to this layout.
38223      * @param {String} target The target region key (north, south, east, west or center).
38224      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38225      * @return {Roo.ContentPanel} The removed panel
38226      */
38227     remove : function(target, panel){
38228         target = target.toLowerCase();
38229         return this.regions[target].remove(panel);
38230     },
38231
38232     /**
38233      * Searches all regions for a panel with the specified id
38234      * @param {String} panelId
38235      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38236      */
38237     findPanel : function(panelId){
38238         var rs = this.regions;
38239         for(var target in rs){
38240             if(typeof rs[target] != "function"){
38241                 var p = rs[target].getPanel(panelId);
38242                 if(p){
38243                     return p;
38244                 }
38245             }
38246         }
38247         return null;
38248     },
38249
38250     /**
38251      * Searches all regions for a panel with the specified id and activates (shows) it.
38252      * @param {String/ContentPanel} panelId The panels id or the panel itself
38253      * @return {Roo.ContentPanel} The shown panel or null
38254      */
38255     showPanel : function(panelId) {
38256       var rs = this.regions;
38257       for(var target in rs){
38258          var r = rs[target];
38259          if(typeof r != "function"){
38260             if(r.hasPanel(panelId)){
38261                return r.showPanel(panelId);
38262             }
38263          }
38264       }
38265       return null;
38266    },
38267
38268    /**
38269      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38270      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38271      */
38272    /*
38273     restoreState : function(provider){
38274         if(!provider){
38275             provider = Roo.state.Manager;
38276         }
38277         var sm = new Roo.LayoutStateManager();
38278         sm.init(this, provider);
38279     },
38280 */
38281  
38282  
38283     /**
38284      * Adds a xtype elements to the layout.
38285      * <pre><code>
38286
38287 layout.addxtype({
38288        xtype : 'ContentPanel',
38289        region: 'west',
38290        items: [ .... ]
38291    }
38292 );
38293
38294 layout.addxtype({
38295         xtype : 'NestedLayoutPanel',
38296         region: 'west',
38297         layout: {
38298            center: { },
38299            west: { }   
38300         },
38301         items : [ ... list of content panels or nested layout panels.. ]
38302    }
38303 );
38304 </code></pre>
38305      * @param {Object} cfg Xtype definition of item to add.
38306      */
38307     addxtype : function(cfg)
38308     {
38309         // basically accepts a pannel...
38310         // can accept a layout region..!?!?
38311         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38312         
38313         
38314         // theory?  children can only be panels??
38315         
38316         //if (!cfg.xtype.match(/Panel$/)) {
38317         //    return false;
38318         //}
38319         var ret = false;
38320         
38321         if (typeof(cfg.region) == 'undefined') {
38322             Roo.log("Failed to add Panel, region was not set");
38323             Roo.log(cfg);
38324             return false;
38325         }
38326         var region = cfg.region;
38327         delete cfg.region;
38328         
38329           
38330         var xitems = [];
38331         if (cfg.items) {
38332             xitems = cfg.items;
38333             delete cfg.items;
38334         }
38335         var nb = false;
38336         
38337         if ( region == 'center') {
38338             Roo.log("Center: " + cfg.title);
38339         }
38340         
38341         
38342         switch(cfg.xtype) 
38343         {
38344             case 'Content':  // ContentPanel (el, cfg)
38345             case 'Scroll':  // ContentPanel (el, cfg)
38346             case 'View': 
38347                 cfg.autoCreate = cfg.autoCreate || true;
38348                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38349                 //} else {
38350                 //    var el = this.el.createChild();
38351                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38352                 //}
38353                 
38354                 this.add(region, ret);
38355                 break;
38356             
38357             /*
38358             case 'TreePanel': // our new panel!
38359                 cfg.el = this.el.createChild();
38360                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38361                 this.add(region, ret);
38362                 break;
38363             */
38364             
38365             case 'Nest': 
38366                 // create a new Layout (which is  a Border Layout...
38367                 
38368                 var clayout = cfg.layout;
38369                 clayout.el  = this.el.createChild();
38370                 clayout.items   = clayout.items  || [];
38371                 
38372                 delete cfg.layout;
38373                 
38374                 // replace this exitems with the clayout ones..
38375                 xitems = clayout.items;
38376                  
38377                 // force background off if it's in center...
38378                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38379                     cfg.background = false;
38380                 }
38381                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38382                 
38383                 
38384                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38385                 //console.log('adding nested layout panel '  + cfg.toSource());
38386                 this.add(region, ret);
38387                 nb = {}; /// find first...
38388                 break;
38389             
38390             case 'Grid':
38391                 
38392                 // needs grid and region
38393                 
38394                 //var el = this.getRegion(region).el.createChild();
38395                 /*
38396                  *var el = this.el.createChild();
38397                 // create the grid first...
38398                 cfg.grid.container = el;
38399                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38400                 */
38401                 
38402                 if (region == 'center' && this.active ) {
38403                     cfg.background = false;
38404                 }
38405                 
38406                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38407                 
38408                 this.add(region, ret);
38409                 /*
38410                 if (cfg.background) {
38411                     // render grid on panel activation (if panel background)
38412                     ret.on('activate', function(gp) {
38413                         if (!gp.grid.rendered) {
38414                     //        gp.grid.render(el);
38415                         }
38416                     });
38417                 } else {
38418                   //  cfg.grid.render(el);
38419                 }
38420                 */
38421                 break;
38422            
38423            
38424             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38425                 // it was the old xcomponent building that caused this before.
38426                 // espeically if border is the top element in the tree.
38427                 ret = this;
38428                 break; 
38429                 
38430                     
38431                 
38432                 
38433                 
38434             default:
38435                 /*
38436                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38437                     
38438                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38439                     this.add(region, ret);
38440                 } else {
38441                 */
38442                     Roo.log(cfg);
38443                     throw "Can not add '" + cfg.xtype + "' to Border";
38444                     return null;
38445              
38446                                 
38447              
38448         }
38449         this.beginUpdate();
38450         // add children..
38451         var region = '';
38452         var abn = {};
38453         Roo.each(xitems, function(i)  {
38454             region = nb && i.region ? i.region : false;
38455             
38456             var add = ret.addxtype(i);
38457            
38458             if (region) {
38459                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38460                 if (!i.background) {
38461                     abn[region] = nb[region] ;
38462                 }
38463             }
38464             
38465         });
38466         this.endUpdate();
38467
38468         // make the last non-background panel active..
38469         //if (nb) { Roo.log(abn); }
38470         if (nb) {
38471             
38472             for(var r in abn) {
38473                 region = this.getRegion(r);
38474                 if (region) {
38475                     // tried using nb[r], but it does not work..
38476                      
38477                     region.showPanel(abn[r]);
38478                    
38479                 }
38480             }
38481         }
38482         return ret;
38483         
38484     },
38485     
38486     
38487 // private
38488     factory : function(cfg)
38489     {
38490         
38491         var validRegions = Roo.bootstrap.layout.Border.regions;
38492
38493         var target = cfg.region;
38494         cfg.mgr = this;
38495         
38496         var r = Roo.bootstrap.layout;
38497         Roo.log(target);
38498         switch(target){
38499             case "north":
38500                 return new r.North(cfg);
38501             case "south":
38502                 return new r.South(cfg);
38503             case "east":
38504                 return new r.East(cfg);
38505             case "west":
38506                 return new r.West(cfg);
38507             case "center":
38508                 return new r.Center(cfg);
38509         }
38510         throw 'Layout region "'+target+'" not supported.';
38511     }
38512     
38513     
38514 });
38515  /*
38516  * Based on:
38517  * Ext JS Library 1.1.1
38518  * Copyright(c) 2006-2007, Ext JS, LLC.
38519  *
38520  * Originally Released Under LGPL - original licence link has changed is not relivant.
38521  *
38522  * Fork - LGPL
38523  * <script type="text/javascript">
38524  */
38525  
38526 /**
38527  * @class Roo.bootstrap.layout.Basic
38528  * @extends Roo.util.Observable
38529  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38530  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38531  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38532  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38533  * @cfg {string}   region  the region that it inhabits..
38534  * @cfg {bool}   skipConfig skip config?
38535  * 
38536
38537  */
38538 Roo.bootstrap.layout.Basic = function(config){
38539     
38540     this.mgr = config.mgr;
38541     
38542     this.position = config.region;
38543     
38544     var skipConfig = config.skipConfig;
38545     
38546     this.events = {
38547         /**
38548          * @scope Roo.BasicLayoutRegion
38549          */
38550         
38551         /**
38552          * @event beforeremove
38553          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38554          * @param {Roo.LayoutRegion} this
38555          * @param {Roo.ContentPanel} panel The panel
38556          * @param {Object} e The cancel event object
38557          */
38558         "beforeremove" : true,
38559         /**
38560          * @event invalidated
38561          * Fires when the layout for this region is changed.
38562          * @param {Roo.LayoutRegion} this
38563          */
38564         "invalidated" : true,
38565         /**
38566          * @event visibilitychange
38567          * Fires when this region is shown or hidden 
38568          * @param {Roo.LayoutRegion} this
38569          * @param {Boolean} visibility true or false
38570          */
38571         "visibilitychange" : true,
38572         /**
38573          * @event paneladded
38574          * Fires when a panel is added. 
38575          * @param {Roo.LayoutRegion} this
38576          * @param {Roo.ContentPanel} panel The panel
38577          */
38578         "paneladded" : true,
38579         /**
38580          * @event panelremoved
38581          * Fires when a panel is removed. 
38582          * @param {Roo.LayoutRegion} this
38583          * @param {Roo.ContentPanel} panel The panel
38584          */
38585         "panelremoved" : true,
38586         /**
38587          * @event beforecollapse
38588          * Fires when this region before collapse.
38589          * @param {Roo.LayoutRegion} this
38590          */
38591         "beforecollapse" : true,
38592         /**
38593          * @event collapsed
38594          * Fires when this region is collapsed.
38595          * @param {Roo.LayoutRegion} this
38596          */
38597         "collapsed" : true,
38598         /**
38599          * @event expanded
38600          * Fires when this region is expanded.
38601          * @param {Roo.LayoutRegion} this
38602          */
38603         "expanded" : true,
38604         /**
38605          * @event slideshow
38606          * Fires when this region is slid into view.
38607          * @param {Roo.LayoutRegion} this
38608          */
38609         "slideshow" : true,
38610         /**
38611          * @event slidehide
38612          * Fires when this region slides out of view. 
38613          * @param {Roo.LayoutRegion} this
38614          */
38615         "slidehide" : true,
38616         /**
38617          * @event panelactivated
38618          * Fires when a panel is activated. 
38619          * @param {Roo.LayoutRegion} this
38620          * @param {Roo.ContentPanel} panel The activated panel
38621          */
38622         "panelactivated" : true,
38623         /**
38624          * @event resized
38625          * Fires when the user resizes this region. 
38626          * @param {Roo.LayoutRegion} this
38627          * @param {Number} newSize The new size (width for east/west, height for north/south)
38628          */
38629         "resized" : true
38630     };
38631     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38632     this.panels = new Roo.util.MixedCollection();
38633     this.panels.getKey = this.getPanelId.createDelegate(this);
38634     this.box = null;
38635     this.activePanel = null;
38636     // ensure listeners are added...
38637     
38638     if (config.listeners || config.events) {
38639         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38640             listeners : config.listeners || {},
38641             events : config.events || {}
38642         });
38643     }
38644     
38645     if(skipConfig !== true){
38646         this.applyConfig(config);
38647     }
38648 };
38649
38650 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38651 {
38652     getPanelId : function(p){
38653         return p.getId();
38654     },
38655     
38656     applyConfig : function(config){
38657         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38658         this.config = config;
38659         
38660     },
38661     
38662     /**
38663      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38664      * the width, for horizontal (north, south) the height.
38665      * @param {Number} newSize The new width or height
38666      */
38667     resizeTo : function(newSize){
38668         var el = this.el ? this.el :
38669                  (this.activePanel ? this.activePanel.getEl() : null);
38670         if(el){
38671             switch(this.position){
38672                 case "east":
38673                 case "west":
38674                     el.setWidth(newSize);
38675                     this.fireEvent("resized", this, newSize);
38676                 break;
38677                 case "north":
38678                 case "south":
38679                     el.setHeight(newSize);
38680                     this.fireEvent("resized", this, newSize);
38681                 break;                
38682             }
38683         }
38684     },
38685     
38686     getBox : function(){
38687         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38688     },
38689     
38690     getMargins : function(){
38691         return this.margins;
38692     },
38693     
38694     updateBox : function(box){
38695         this.box = box;
38696         var el = this.activePanel.getEl();
38697         el.dom.style.left = box.x + "px";
38698         el.dom.style.top = box.y + "px";
38699         this.activePanel.setSize(box.width, box.height);
38700     },
38701     
38702     /**
38703      * Returns the container element for this region.
38704      * @return {Roo.Element}
38705      */
38706     getEl : function(){
38707         return this.activePanel;
38708     },
38709     
38710     /**
38711      * Returns true if this region is currently visible.
38712      * @return {Boolean}
38713      */
38714     isVisible : function(){
38715         return this.activePanel ? true : false;
38716     },
38717     
38718     setActivePanel : function(panel){
38719         panel = this.getPanel(panel);
38720         if(this.activePanel && this.activePanel != panel){
38721             this.activePanel.setActiveState(false);
38722             this.activePanel.getEl().setLeftTop(-10000,-10000);
38723         }
38724         this.activePanel = panel;
38725         panel.setActiveState(true);
38726         if(this.box){
38727             panel.setSize(this.box.width, this.box.height);
38728         }
38729         this.fireEvent("panelactivated", this, panel);
38730         this.fireEvent("invalidated");
38731     },
38732     
38733     /**
38734      * Show the specified panel.
38735      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38736      * @return {Roo.ContentPanel} The shown panel or null
38737      */
38738     showPanel : function(panel){
38739         panel = this.getPanel(panel);
38740         if(panel){
38741             this.setActivePanel(panel);
38742         }
38743         return panel;
38744     },
38745     
38746     /**
38747      * Get the active panel for this region.
38748      * @return {Roo.ContentPanel} The active panel or null
38749      */
38750     getActivePanel : function(){
38751         return this.activePanel;
38752     },
38753     
38754     /**
38755      * Add the passed ContentPanel(s)
38756      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38757      * @return {Roo.ContentPanel} The panel added (if only one was added)
38758      */
38759     add : function(panel){
38760         if(arguments.length > 1){
38761             for(var i = 0, len = arguments.length; i < len; i++) {
38762                 this.add(arguments[i]);
38763             }
38764             return null;
38765         }
38766         if(this.hasPanel(panel)){
38767             this.showPanel(panel);
38768             return panel;
38769         }
38770         var el = panel.getEl();
38771         if(el.dom.parentNode != this.mgr.el.dom){
38772             this.mgr.el.dom.appendChild(el.dom);
38773         }
38774         if(panel.setRegion){
38775             panel.setRegion(this);
38776         }
38777         this.panels.add(panel);
38778         el.setStyle("position", "absolute");
38779         if(!panel.background){
38780             this.setActivePanel(panel);
38781             if(this.config.initialSize && this.panels.getCount()==1){
38782                 this.resizeTo(this.config.initialSize);
38783             }
38784         }
38785         this.fireEvent("paneladded", this, panel);
38786         return panel;
38787     },
38788     
38789     /**
38790      * Returns true if the panel is in this region.
38791      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38792      * @return {Boolean}
38793      */
38794     hasPanel : function(panel){
38795         if(typeof panel == "object"){ // must be panel obj
38796             panel = panel.getId();
38797         }
38798         return this.getPanel(panel) ? true : false;
38799     },
38800     
38801     /**
38802      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38803      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38804      * @param {Boolean} preservePanel Overrides the config preservePanel option
38805      * @return {Roo.ContentPanel} The panel that was removed
38806      */
38807     remove : function(panel, preservePanel){
38808         panel = this.getPanel(panel);
38809         if(!panel){
38810             return null;
38811         }
38812         var e = {};
38813         this.fireEvent("beforeremove", this, panel, e);
38814         if(e.cancel === true){
38815             return null;
38816         }
38817         var panelId = panel.getId();
38818         this.panels.removeKey(panelId);
38819         return panel;
38820     },
38821     
38822     /**
38823      * Returns the panel specified or null if it's not in this region.
38824      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38825      * @return {Roo.ContentPanel}
38826      */
38827     getPanel : function(id){
38828         if(typeof id == "object"){ // must be panel obj
38829             return id;
38830         }
38831         return this.panels.get(id);
38832     },
38833     
38834     /**
38835      * Returns this regions position (north/south/east/west/center).
38836      * @return {String} 
38837      */
38838     getPosition: function(){
38839         return this.position;    
38840     }
38841 });/*
38842  * Based on:
38843  * Ext JS Library 1.1.1
38844  * Copyright(c) 2006-2007, Ext JS, LLC.
38845  *
38846  * Originally Released Under LGPL - original licence link has changed is not relivant.
38847  *
38848  * Fork - LGPL
38849  * <script type="text/javascript">
38850  */
38851  
38852 /**
38853  * @class Roo.bootstrap.layout.Region
38854  * @extends Roo.bootstrap.layout.Basic
38855  * This class represents a region in a layout manager.
38856  
38857  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38858  * @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})
38859  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38860  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38861  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38862  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38863  * @cfg {String}    title           The title for the region (overrides panel titles)
38864  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38865  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38866  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38867  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38868  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38869  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38870  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38871  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38872  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38873  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38874
38875  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38876  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38877  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38878  * @cfg {Number}    width           For East/West panels
38879  * @cfg {Number}    height          For North/South panels
38880  * @cfg {Boolean}   split           To show the splitter
38881  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38882  * 
38883  * @cfg {string}   cls             Extra CSS classes to add to region
38884  * 
38885  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38886  * @cfg {string}   region  the region that it inhabits..
38887  *
38888
38889  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38890  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38891
38892  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38893  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38894  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38895  */
38896 Roo.bootstrap.layout.Region = function(config)
38897 {
38898     this.applyConfig(config);
38899
38900     var mgr = config.mgr;
38901     var pos = config.region;
38902     config.skipConfig = true;
38903     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38904     
38905     if (mgr.el) {
38906         this.onRender(mgr.el);   
38907     }
38908      
38909     this.visible = true;
38910     this.collapsed = false;
38911     this.unrendered_panels = [];
38912 };
38913
38914 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38915
38916     position: '', // set by wrapper (eg. north/south etc..)
38917     unrendered_panels : null,  // unrendered panels.
38918     
38919     tabPosition : false,
38920     
38921     mgr: false, // points to 'Border'
38922     
38923     
38924     createBody : function(){
38925         /** This region's body element 
38926         * @type Roo.Element */
38927         this.bodyEl = this.el.createChild({
38928                 tag: "div",
38929                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38930         });
38931     },
38932
38933     onRender: function(ctr, pos)
38934     {
38935         var dh = Roo.DomHelper;
38936         /** This region's container element 
38937         * @type Roo.Element */
38938         this.el = dh.append(ctr.dom, {
38939                 tag: "div",
38940                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38941             }, true);
38942         /** This region's title element 
38943         * @type Roo.Element */
38944     
38945         this.titleEl = dh.append(this.el.dom,  {
38946                 tag: "div",
38947                 unselectable: "on",
38948                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38949                 children:[
38950                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38951                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38952                 ]
38953             }, true);
38954         
38955         this.titleEl.enableDisplayMode();
38956         /** This region's title text element 
38957         * @type HTMLElement */
38958         this.titleTextEl = this.titleEl.dom.firstChild;
38959         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38960         /*
38961         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38962         this.closeBtn.enableDisplayMode();
38963         this.closeBtn.on("click", this.closeClicked, this);
38964         this.closeBtn.hide();
38965     */
38966         this.createBody(this.config);
38967         if(this.config.hideWhenEmpty){
38968             this.hide();
38969             this.on("paneladded", this.validateVisibility, this);
38970             this.on("panelremoved", this.validateVisibility, this);
38971         }
38972         if(this.autoScroll){
38973             this.bodyEl.setStyle("overflow", "auto");
38974         }else{
38975             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38976         }
38977         //if(c.titlebar !== false){
38978             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38979                 this.titleEl.hide();
38980             }else{
38981                 this.titleEl.show();
38982                 if(this.config.title){
38983                     this.titleTextEl.innerHTML = this.config.title;
38984                 }
38985             }
38986         //}
38987         if(this.config.collapsed){
38988             this.collapse(true);
38989         }
38990         if(this.config.hidden){
38991             this.hide();
38992         }
38993         
38994         if (this.unrendered_panels && this.unrendered_panels.length) {
38995             for (var i =0;i< this.unrendered_panels.length; i++) {
38996                 this.add(this.unrendered_panels[i]);
38997             }
38998             this.unrendered_panels = null;
38999             
39000         }
39001         
39002     },
39003     
39004     applyConfig : function(c)
39005     {
39006         /*
39007          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39008             var dh = Roo.DomHelper;
39009             if(c.titlebar !== false){
39010                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39011                 this.collapseBtn.on("click", this.collapse, this);
39012                 this.collapseBtn.enableDisplayMode();
39013                 /*
39014                 if(c.showPin === true || this.showPin){
39015                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39016                     this.stickBtn.enableDisplayMode();
39017                     this.stickBtn.on("click", this.expand, this);
39018                     this.stickBtn.hide();
39019                 }
39020                 
39021             }
39022             */
39023             /** This region's collapsed element
39024             * @type Roo.Element */
39025             /*
39026              *
39027             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39028                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39029             ]}, true);
39030             
39031             if(c.floatable !== false){
39032                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39033                this.collapsedEl.on("click", this.collapseClick, this);
39034             }
39035
39036             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39037                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39038                    id: "message", unselectable: "on", style:{"float":"left"}});
39039                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39040              }
39041             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39042             this.expandBtn.on("click", this.expand, this);
39043             
39044         }
39045         
39046         if(this.collapseBtn){
39047             this.collapseBtn.setVisible(c.collapsible == true);
39048         }
39049         
39050         this.cmargins = c.cmargins || this.cmargins ||
39051                          (this.position == "west" || this.position == "east" ?
39052                              {top: 0, left: 2, right:2, bottom: 0} :
39053                              {top: 2, left: 0, right:0, bottom: 2});
39054         */
39055         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39056         
39057         
39058         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39059         
39060         this.autoScroll = c.autoScroll || false;
39061         
39062         
39063        
39064         
39065         this.duration = c.duration || .30;
39066         this.slideDuration = c.slideDuration || .45;
39067         this.config = c;
39068        
39069     },
39070     /**
39071      * Returns true if this region is currently visible.
39072      * @return {Boolean}
39073      */
39074     isVisible : function(){
39075         return this.visible;
39076     },
39077
39078     /**
39079      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39080      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39081      */
39082     //setCollapsedTitle : function(title){
39083     //    title = title || "&#160;";
39084      //   if(this.collapsedTitleTextEl){
39085       //      this.collapsedTitleTextEl.innerHTML = title;
39086        // }
39087     //},
39088
39089     getBox : function(){
39090         var b;
39091       //  if(!this.collapsed){
39092             b = this.el.getBox(false, true);
39093        // }else{
39094           //  b = this.collapsedEl.getBox(false, true);
39095         //}
39096         return b;
39097     },
39098
39099     getMargins : function(){
39100         return this.margins;
39101         //return this.collapsed ? this.cmargins : this.margins;
39102     },
39103 /*
39104     highlight : function(){
39105         this.el.addClass("x-layout-panel-dragover");
39106     },
39107
39108     unhighlight : function(){
39109         this.el.removeClass("x-layout-panel-dragover");
39110     },
39111 */
39112     updateBox : function(box)
39113     {
39114         if (!this.bodyEl) {
39115             return; // not rendered yet..
39116         }
39117         
39118         this.box = box;
39119         if(!this.collapsed){
39120             this.el.dom.style.left = box.x + "px";
39121             this.el.dom.style.top = box.y + "px";
39122             this.updateBody(box.width, box.height);
39123         }else{
39124             this.collapsedEl.dom.style.left = box.x + "px";
39125             this.collapsedEl.dom.style.top = box.y + "px";
39126             this.collapsedEl.setSize(box.width, box.height);
39127         }
39128         if(this.tabs){
39129             this.tabs.autoSizeTabs();
39130         }
39131     },
39132
39133     updateBody : function(w, h)
39134     {
39135         if(w !== null){
39136             this.el.setWidth(w);
39137             w -= this.el.getBorderWidth("rl");
39138             if(this.config.adjustments){
39139                 w += this.config.adjustments[0];
39140             }
39141         }
39142         if(h !== null && h > 0){
39143             this.el.setHeight(h);
39144             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39145             h -= this.el.getBorderWidth("tb");
39146             if(this.config.adjustments){
39147                 h += this.config.adjustments[1];
39148             }
39149             this.bodyEl.setHeight(h);
39150             if(this.tabs){
39151                 h = this.tabs.syncHeight(h);
39152             }
39153         }
39154         if(this.panelSize){
39155             w = w !== null ? w : this.panelSize.width;
39156             h = h !== null ? h : this.panelSize.height;
39157         }
39158         if(this.activePanel){
39159             var el = this.activePanel.getEl();
39160             w = w !== null ? w : el.getWidth();
39161             h = h !== null ? h : el.getHeight();
39162             this.panelSize = {width: w, height: h};
39163             this.activePanel.setSize(w, h);
39164         }
39165         if(Roo.isIE && this.tabs){
39166             this.tabs.el.repaint();
39167         }
39168     },
39169
39170     /**
39171      * Returns the container element for this region.
39172      * @return {Roo.Element}
39173      */
39174     getEl : function(){
39175         return this.el;
39176     },
39177
39178     /**
39179      * Hides this region.
39180      */
39181     hide : function(){
39182         //if(!this.collapsed){
39183             this.el.dom.style.left = "-2000px";
39184             this.el.hide();
39185         //}else{
39186          //   this.collapsedEl.dom.style.left = "-2000px";
39187          //   this.collapsedEl.hide();
39188        // }
39189         this.visible = false;
39190         this.fireEvent("visibilitychange", this, false);
39191     },
39192
39193     /**
39194      * Shows this region if it was previously hidden.
39195      */
39196     show : function(){
39197         //if(!this.collapsed){
39198             this.el.show();
39199         //}else{
39200         //    this.collapsedEl.show();
39201        // }
39202         this.visible = true;
39203         this.fireEvent("visibilitychange", this, true);
39204     },
39205 /*
39206     closeClicked : function(){
39207         if(this.activePanel){
39208             this.remove(this.activePanel);
39209         }
39210     },
39211
39212     collapseClick : function(e){
39213         if(this.isSlid){
39214            e.stopPropagation();
39215            this.slideIn();
39216         }else{
39217            e.stopPropagation();
39218            this.slideOut();
39219         }
39220     },
39221 */
39222     /**
39223      * Collapses this region.
39224      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39225      */
39226     /*
39227     collapse : function(skipAnim, skipCheck = false){
39228         if(this.collapsed) {
39229             return;
39230         }
39231         
39232         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39233             
39234             this.collapsed = true;
39235             if(this.split){
39236                 this.split.el.hide();
39237             }
39238             if(this.config.animate && skipAnim !== true){
39239                 this.fireEvent("invalidated", this);
39240                 this.animateCollapse();
39241             }else{
39242                 this.el.setLocation(-20000,-20000);
39243                 this.el.hide();
39244                 this.collapsedEl.show();
39245                 this.fireEvent("collapsed", this);
39246                 this.fireEvent("invalidated", this);
39247             }
39248         }
39249         
39250     },
39251 */
39252     animateCollapse : function(){
39253         // overridden
39254     },
39255
39256     /**
39257      * Expands this region if it was previously collapsed.
39258      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39259      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39260      */
39261     /*
39262     expand : function(e, skipAnim){
39263         if(e) {
39264             e.stopPropagation();
39265         }
39266         if(!this.collapsed || this.el.hasActiveFx()) {
39267             return;
39268         }
39269         if(this.isSlid){
39270             this.afterSlideIn();
39271             skipAnim = true;
39272         }
39273         this.collapsed = false;
39274         if(this.config.animate && skipAnim !== true){
39275             this.animateExpand();
39276         }else{
39277             this.el.show();
39278             if(this.split){
39279                 this.split.el.show();
39280             }
39281             this.collapsedEl.setLocation(-2000,-2000);
39282             this.collapsedEl.hide();
39283             this.fireEvent("invalidated", this);
39284             this.fireEvent("expanded", this);
39285         }
39286     },
39287 */
39288     animateExpand : function(){
39289         // overridden
39290     },
39291
39292     initTabs : function()
39293     {
39294         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39295         
39296         var ts = new Roo.bootstrap.panel.Tabs({
39297             el: this.bodyEl.dom,
39298             region : this,
39299             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39300             disableTooltips: this.config.disableTabTips,
39301             toolbar : this.config.toolbar
39302         });
39303         
39304         if(this.config.hideTabs){
39305             ts.stripWrap.setDisplayed(false);
39306         }
39307         this.tabs = ts;
39308         ts.resizeTabs = this.config.resizeTabs === true;
39309         ts.minTabWidth = this.config.minTabWidth || 40;
39310         ts.maxTabWidth = this.config.maxTabWidth || 250;
39311         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39312         ts.monitorResize = false;
39313         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39314         ts.bodyEl.addClass('roo-layout-tabs-body');
39315         this.panels.each(this.initPanelAsTab, this);
39316     },
39317
39318     initPanelAsTab : function(panel){
39319         var ti = this.tabs.addTab(
39320             panel.getEl().id,
39321             panel.getTitle(),
39322             null,
39323             this.config.closeOnTab && panel.isClosable(),
39324             panel.tpl
39325         );
39326         if(panel.tabTip !== undefined){
39327             ti.setTooltip(panel.tabTip);
39328         }
39329         ti.on("activate", function(){
39330               this.setActivePanel(panel);
39331         }, this);
39332         
39333         if(this.config.closeOnTab){
39334             ti.on("beforeclose", function(t, e){
39335                 e.cancel = true;
39336                 this.remove(panel);
39337             }, this);
39338         }
39339         
39340         panel.tabItem = ti;
39341         
39342         return ti;
39343     },
39344
39345     updatePanelTitle : function(panel, title)
39346     {
39347         if(this.activePanel == panel){
39348             this.updateTitle(title);
39349         }
39350         if(this.tabs){
39351             var ti = this.tabs.getTab(panel.getEl().id);
39352             ti.setText(title);
39353             if(panel.tabTip !== undefined){
39354                 ti.setTooltip(panel.tabTip);
39355             }
39356         }
39357     },
39358
39359     updateTitle : function(title){
39360         if(this.titleTextEl && !this.config.title){
39361             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39362         }
39363     },
39364
39365     setActivePanel : function(panel)
39366     {
39367         panel = this.getPanel(panel);
39368         if(this.activePanel && this.activePanel != panel){
39369             if(this.activePanel.setActiveState(false) === false){
39370                 return;
39371             }
39372         }
39373         this.activePanel = panel;
39374         panel.setActiveState(true);
39375         if(this.panelSize){
39376             panel.setSize(this.panelSize.width, this.panelSize.height);
39377         }
39378         if(this.closeBtn){
39379             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39380         }
39381         this.updateTitle(panel.getTitle());
39382         if(this.tabs){
39383             this.fireEvent("invalidated", this);
39384         }
39385         this.fireEvent("panelactivated", this, panel);
39386     },
39387
39388     /**
39389      * Shows the specified panel.
39390      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39391      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39392      */
39393     showPanel : function(panel)
39394     {
39395         panel = this.getPanel(panel);
39396         if(panel){
39397             if(this.tabs){
39398                 var tab = this.tabs.getTab(panel.getEl().id);
39399                 if(tab.isHidden()){
39400                     this.tabs.unhideTab(tab.id);
39401                 }
39402                 tab.activate();
39403             }else{
39404                 this.setActivePanel(panel);
39405             }
39406         }
39407         return panel;
39408     },
39409
39410     /**
39411      * Get the active panel for this region.
39412      * @return {Roo.ContentPanel} The active panel or null
39413      */
39414     getActivePanel : function(){
39415         return this.activePanel;
39416     },
39417
39418     validateVisibility : function(){
39419         if(this.panels.getCount() < 1){
39420             this.updateTitle("&#160;");
39421             this.closeBtn.hide();
39422             this.hide();
39423         }else{
39424             if(!this.isVisible()){
39425                 this.show();
39426             }
39427         }
39428     },
39429
39430     /**
39431      * Adds the passed ContentPanel(s) to this region.
39432      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39433      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39434      */
39435     add : function(panel)
39436     {
39437         if(arguments.length > 1){
39438             for(var i = 0, len = arguments.length; i < len; i++) {
39439                 this.add(arguments[i]);
39440             }
39441             return null;
39442         }
39443         
39444         // if we have not been rendered yet, then we can not really do much of this..
39445         if (!this.bodyEl) {
39446             this.unrendered_panels.push(panel);
39447             return panel;
39448         }
39449         
39450         
39451         
39452         
39453         if(this.hasPanel(panel)){
39454             this.showPanel(panel);
39455             return panel;
39456         }
39457         panel.setRegion(this);
39458         this.panels.add(panel);
39459        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39460             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39461             // and hide them... ???
39462             this.bodyEl.dom.appendChild(panel.getEl().dom);
39463             if(panel.background !== true){
39464                 this.setActivePanel(panel);
39465             }
39466             this.fireEvent("paneladded", this, panel);
39467             return panel;
39468         }
39469         */
39470         if(!this.tabs){
39471             this.initTabs();
39472         }else{
39473             this.initPanelAsTab(panel);
39474         }
39475         
39476         
39477         if(panel.background !== true){
39478             this.tabs.activate(panel.getEl().id);
39479         }
39480         this.fireEvent("paneladded", this, panel);
39481         return panel;
39482     },
39483
39484     /**
39485      * Hides the tab for the specified panel.
39486      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39487      */
39488     hidePanel : function(panel){
39489         if(this.tabs && (panel = this.getPanel(panel))){
39490             this.tabs.hideTab(panel.getEl().id);
39491         }
39492     },
39493
39494     /**
39495      * Unhides the tab for a previously hidden panel.
39496      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39497      */
39498     unhidePanel : function(panel){
39499         if(this.tabs && (panel = this.getPanel(panel))){
39500             this.tabs.unhideTab(panel.getEl().id);
39501         }
39502     },
39503
39504     clearPanels : function(){
39505         while(this.panels.getCount() > 0){
39506              this.remove(this.panels.first());
39507         }
39508     },
39509
39510     /**
39511      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39512      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39513      * @param {Boolean} preservePanel Overrides the config preservePanel option
39514      * @return {Roo.ContentPanel} The panel that was removed
39515      */
39516     remove : function(panel, preservePanel)
39517     {
39518         panel = this.getPanel(panel);
39519         if(!panel){
39520             return null;
39521         }
39522         var e = {};
39523         this.fireEvent("beforeremove", this, panel, e);
39524         if(e.cancel === true){
39525             return null;
39526         }
39527         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39528         var panelId = panel.getId();
39529         this.panels.removeKey(panelId);
39530         if(preservePanel){
39531             document.body.appendChild(panel.getEl().dom);
39532         }
39533         if(this.tabs){
39534             this.tabs.removeTab(panel.getEl().id);
39535         }else if (!preservePanel){
39536             this.bodyEl.dom.removeChild(panel.getEl().dom);
39537         }
39538         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39539             var p = this.panels.first();
39540             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39541             tempEl.appendChild(p.getEl().dom);
39542             this.bodyEl.update("");
39543             this.bodyEl.dom.appendChild(p.getEl().dom);
39544             tempEl = null;
39545             this.updateTitle(p.getTitle());
39546             this.tabs = null;
39547             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39548             this.setActivePanel(p);
39549         }
39550         panel.setRegion(null);
39551         if(this.activePanel == panel){
39552             this.activePanel = null;
39553         }
39554         if(this.config.autoDestroy !== false && preservePanel !== true){
39555             try{panel.destroy();}catch(e){}
39556         }
39557         this.fireEvent("panelremoved", this, panel);
39558         return panel;
39559     },
39560
39561     /**
39562      * Returns the TabPanel component used by this region
39563      * @return {Roo.TabPanel}
39564      */
39565     getTabs : function(){
39566         return this.tabs;
39567     },
39568
39569     createTool : function(parentEl, className){
39570         var btn = Roo.DomHelper.append(parentEl, {
39571             tag: "div",
39572             cls: "x-layout-tools-button",
39573             children: [ {
39574                 tag: "div",
39575                 cls: "roo-layout-tools-button-inner " + className,
39576                 html: "&#160;"
39577             }]
39578         }, true);
39579         btn.addClassOnOver("roo-layout-tools-button-over");
39580         return btn;
39581     }
39582 });/*
39583  * Based on:
39584  * Ext JS Library 1.1.1
39585  * Copyright(c) 2006-2007, Ext JS, LLC.
39586  *
39587  * Originally Released Under LGPL - original licence link has changed is not relivant.
39588  *
39589  * Fork - LGPL
39590  * <script type="text/javascript">
39591  */
39592  
39593
39594
39595 /**
39596  * @class Roo.SplitLayoutRegion
39597  * @extends Roo.LayoutRegion
39598  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39599  */
39600 Roo.bootstrap.layout.Split = function(config){
39601     this.cursor = config.cursor;
39602     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39603 };
39604
39605 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39606 {
39607     splitTip : "Drag to resize.",
39608     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39609     useSplitTips : false,
39610
39611     applyConfig : function(config){
39612         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39613     },
39614     
39615     onRender : function(ctr,pos) {
39616         
39617         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39618         if(!this.config.split){
39619             return;
39620         }
39621         if(!this.split){
39622             
39623             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39624                             tag: "div",
39625                             id: this.el.id + "-split",
39626                             cls: "roo-layout-split roo-layout-split-"+this.position,
39627                             html: "&#160;"
39628             });
39629             /** The SplitBar for this region 
39630             * @type Roo.SplitBar */
39631             // does not exist yet...
39632             Roo.log([this.position, this.orientation]);
39633             
39634             this.split = new Roo.bootstrap.SplitBar({
39635                 dragElement : splitEl,
39636                 resizingElement: this.el,
39637                 orientation : this.orientation
39638             });
39639             
39640             this.split.on("moved", this.onSplitMove, this);
39641             this.split.useShim = this.config.useShim === true;
39642             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39643             if(this.useSplitTips){
39644                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39645             }
39646             //if(config.collapsible){
39647             //    this.split.el.on("dblclick", this.collapse,  this);
39648             //}
39649         }
39650         if(typeof this.config.minSize != "undefined"){
39651             this.split.minSize = this.config.minSize;
39652         }
39653         if(typeof this.config.maxSize != "undefined"){
39654             this.split.maxSize = this.config.maxSize;
39655         }
39656         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39657             this.hideSplitter();
39658         }
39659         
39660     },
39661
39662     getHMaxSize : function(){
39663          var cmax = this.config.maxSize || 10000;
39664          var center = this.mgr.getRegion("center");
39665          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39666     },
39667
39668     getVMaxSize : function(){
39669          var cmax = this.config.maxSize || 10000;
39670          var center = this.mgr.getRegion("center");
39671          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39672     },
39673
39674     onSplitMove : function(split, newSize){
39675         this.fireEvent("resized", this, newSize);
39676     },
39677     
39678     /** 
39679      * Returns the {@link Roo.SplitBar} for this region.
39680      * @return {Roo.SplitBar}
39681      */
39682     getSplitBar : function(){
39683         return this.split;
39684     },
39685     
39686     hide : function(){
39687         this.hideSplitter();
39688         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39689     },
39690
39691     hideSplitter : function(){
39692         if(this.split){
39693             this.split.el.setLocation(-2000,-2000);
39694             this.split.el.hide();
39695         }
39696     },
39697
39698     show : function(){
39699         if(this.split){
39700             this.split.el.show();
39701         }
39702         Roo.bootstrap.layout.Split.superclass.show.call(this);
39703     },
39704     
39705     beforeSlide: function(){
39706         if(Roo.isGecko){// firefox overflow auto bug workaround
39707             this.bodyEl.clip();
39708             if(this.tabs) {
39709                 this.tabs.bodyEl.clip();
39710             }
39711             if(this.activePanel){
39712                 this.activePanel.getEl().clip();
39713                 
39714                 if(this.activePanel.beforeSlide){
39715                     this.activePanel.beforeSlide();
39716                 }
39717             }
39718         }
39719     },
39720     
39721     afterSlide : function(){
39722         if(Roo.isGecko){// firefox overflow auto bug workaround
39723             this.bodyEl.unclip();
39724             if(this.tabs) {
39725                 this.tabs.bodyEl.unclip();
39726             }
39727             if(this.activePanel){
39728                 this.activePanel.getEl().unclip();
39729                 if(this.activePanel.afterSlide){
39730                     this.activePanel.afterSlide();
39731                 }
39732             }
39733         }
39734     },
39735
39736     initAutoHide : function(){
39737         if(this.autoHide !== false){
39738             if(!this.autoHideHd){
39739                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39740                 this.autoHideHd = {
39741                     "mouseout": function(e){
39742                         if(!e.within(this.el, true)){
39743                             st.delay(500);
39744                         }
39745                     },
39746                     "mouseover" : function(e){
39747                         st.cancel();
39748                     },
39749                     scope : this
39750                 };
39751             }
39752             this.el.on(this.autoHideHd);
39753         }
39754     },
39755
39756     clearAutoHide : function(){
39757         if(this.autoHide !== false){
39758             this.el.un("mouseout", this.autoHideHd.mouseout);
39759             this.el.un("mouseover", this.autoHideHd.mouseover);
39760         }
39761     },
39762
39763     clearMonitor : function(){
39764         Roo.get(document).un("click", this.slideInIf, this);
39765     },
39766
39767     // these names are backwards but not changed for compat
39768     slideOut : function(){
39769         if(this.isSlid || this.el.hasActiveFx()){
39770             return;
39771         }
39772         this.isSlid = true;
39773         if(this.collapseBtn){
39774             this.collapseBtn.hide();
39775         }
39776         this.closeBtnState = this.closeBtn.getStyle('display');
39777         this.closeBtn.hide();
39778         if(this.stickBtn){
39779             this.stickBtn.show();
39780         }
39781         this.el.show();
39782         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39783         this.beforeSlide();
39784         this.el.setStyle("z-index", 10001);
39785         this.el.slideIn(this.getSlideAnchor(), {
39786             callback: function(){
39787                 this.afterSlide();
39788                 this.initAutoHide();
39789                 Roo.get(document).on("click", this.slideInIf, this);
39790                 this.fireEvent("slideshow", this);
39791             },
39792             scope: this,
39793             block: true
39794         });
39795     },
39796
39797     afterSlideIn : function(){
39798         this.clearAutoHide();
39799         this.isSlid = false;
39800         this.clearMonitor();
39801         this.el.setStyle("z-index", "");
39802         if(this.collapseBtn){
39803             this.collapseBtn.show();
39804         }
39805         this.closeBtn.setStyle('display', this.closeBtnState);
39806         if(this.stickBtn){
39807             this.stickBtn.hide();
39808         }
39809         this.fireEvent("slidehide", this);
39810     },
39811
39812     slideIn : function(cb){
39813         if(!this.isSlid || this.el.hasActiveFx()){
39814             Roo.callback(cb);
39815             return;
39816         }
39817         this.isSlid = false;
39818         this.beforeSlide();
39819         this.el.slideOut(this.getSlideAnchor(), {
39820             callback: function(){
39821                 this.el.setLeftTop(-10000, -10000);
39822                 this.afterSlide();
39823                 this.afterSlideIn();
39824                 Roo.callback(cb);
39825             },
39826             scope: this,
39827             block: true
39828         });
39829     },
39830     
39831     slideInIf : function(e){
39832         if(!e.within(this.el)){
39833             this.slideIn();
39834         }
39835     },
39836
39837     animateCollapse : function(){
39838         this.beforeSlide();
39839         this.el.setStyle("z-index", 20000);
39840         var anchor = this.getSlideAnchor();
39841         this.el.slideOut(anchor, {
39842             callback : function(){
39843                 this.el.setStyle("z-index", "");
39844                 this.collapsedEl.slideIn(anchor, {duration:.3});
39845                 this.afterSlide();
39846                 this.el.setLocation(-10000,-10000);
39847                 this.el.hide();
39848                 this.fireEvent("collapsed", this);
39849             },
39850             scope: this,
39851             block: true
39852         });
39853     },
39854
39855     animateExpand : function(){
39856         this.beforeSlide();
39857         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39858         this.el.setStyle("z-index", 20000);
39859         this.collapsedEl.hide({
39860             duration:.1
39861         });
39862         this.el.slideIn(this.getSlideAnchor(), {
39863             callback : function(){
39864                 this.el.setStyle("z-index", "");
39865                 this.afterSlide();
39866                 if(this.split){
39867                     this.split.el.show();
39868                 }
39869                 this.fireEvent("invalidated", this);
39870                 this.fireEvent("expanded", this);
39871             },
39872             scope: this,
39873             block: true
39874         });
39875     },
39876
39877     anchors : {
39878         "west" : "left",
39879         "east" : "right",
39880         "north" : "top",
39881         "south" : "bottom"
39882     },
39883
39884     sanchors : {
39885         "west" : "l",
39886         "east" : "r",
39887         "north" : "t",
39888         "south" : "b"
39889     },
39890
39891     canchors : {
39892         "west" : "tl-tr",
39893         "east" : "tr-tl",
39894         "north" : "tl-bl",
39895         "south" : "bl-tl"
39896     },
39897
39898     getAnchor : function(){
39899         return this.anchors[this.position];
39900     },
39901
39902     getCollapseAnchor : function(){
39903         return this.canchors[this.position];
39904     },
39905
39906     getSlideAnchor : function(){
39907         return this.sanchors[this.position];
39908     },
39909
39910     getAlignAdj : function(){
39911         var cm = this.cmargins;
39912         switch(this.position){
39913             case "west":
39914                 return [0, 0];
39915             break;
39916             case "east":
39917                 return [0, 0];
39918             break;
39919             case "north":
39920                 return [0, 0];
39921             break;
39922             case "south":
39923                 return [0, 0];
39924             break;
39925         }
39926     },
39927
39928     getExpandAdj : function(){
39929         var c = this.collapsedEl, cm = this.cmargins;
39930         switch(this.position){
39931             case "west":
39932                 return [-(cm.right+c.getWidth()+cm.left), 0];
39933             break;
39934             case "east":
39935                 return [cm.right+c.getWidth()+cm.left, 0];
39936             break;
39937             case "north":
39938                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39939             break;
39940             case "south":
39941                 return [0, cm.top+cm.bottom+c.getHeight()];
39942             break;
39943         }
39944     }
39945 });/*
39946  * Based on:
39947  * Ext JS Library 1.1.1
39948  * Copyright(c) 2006-2007, Ext JS, LLC.
39949  *
39950  * Originally Released Under LGPL - original licence link has changed is not relivant.
39951  *
39952  * Fork - LGPL
39953  * <script type="text/javascript">
39954  */
39955 /*
39956  * These classes are private internal classes
39957  */
39958 Roo.bootstrap.layout.Center = function(config){
39959     config.region = "center";
39960     Roo.bootstrap.layout.Region.call(this, config);
39961     this.visible = true;
39962     this.minWidth = config.minWidth || 20;
39963     this.minHeight = config.minHeight || 20;
39964 };
39965
39966 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39967     hide : function(){
39968         // center panel can't be hidden
39969     },
39970     
39971     show : function(){
39972         // center panel can't be hidden
39973     },
39974     
39975     getMinWidth: function(){
39976         return this.minWidth;
39977     },
39978     
39979     getMinHeight: function(){
39980         return this.minHeight;
39981     }
39982 });
39983
39984
39985
39986
39987  
39988
39989
39990
39991
39992
39993
39994 Roo.bootstrap.layout.North = function(config)
39995 {
39996     config.region = 'north';
39997     config.cursor = 'n-resize';
39998     
39999     Roo.bootstrap.layout.Split.call(this, config);
40000     
40001     
40002     if(this.split){
40003         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40004         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40005         this.split.el.addClass("roo-layout-split-v");
40006     }
40007     //var size = config.initialSize || config.height;
40008     //if(this.el && typeof size != "undefined"){
40009     //    this.el.setHeight(size);
40010     //}
40011 };
40012 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40013 {
40014     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40015      
40016      
40017     onRender : function(ctr, pos)
40018     {
40019         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40020         var size = this.config.initialSize || this.config.height;
40021         if(this.el && typeof size != "undefined"){
40022             this.el.setHeight(size);
40023         }
40024     
40025     },
40026     
40027     getBox : function(){
40028         if(this.collapsed){
40029             return this.collapsedEl.getBox();
40030         }
40031         var box = this.el.getBox();
40032         if(this.split){
40033             box.height += this.split.el.getHeight();
40034         }
40035         return box;
40036     },
40037     
40038     updateBox : function(box){
40039         if(this.split && !this.collapsed){
40040             box.height -= this.split.el.getHeight();
40041             this.split.el.setLeft(box.x);
40042             this.split.el.setTop(box.y+box.height);
40043             this.split.el.setWidth(box.width);
40044         }
40045         if(this.collapsed){
40046             this.updateBody(box.width, null);
40047         }
40048         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40049     }
40050 });
40051
40052
40053
40054
40055
40056 Roo.bootstrap.layout.South = function(config){
40057     config.region = 'south';
40058     config.cursor = 's-resize';
40059     Roo.bootstrap.layout.Split.call(this, config);
40060     if(this.split){
40061         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40062         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40063         this.split.el.addClass("roo-layout-split-v");
40064     }
40065     
40066 };
40067
40068 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40069     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40070     
40071     onRender : function(ctr, pos)
40072     {
40073         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40074         var size = this.config.initialSize || this.config.height;
40075         if(this.el && typeof size != "undefined"){
40076             this.el.setHeight(size);
40077         }
40078     
40079     },
40080     
40081     getBox : function(){
40082         if(this.collapsed){
40083             return this.collapsedEl.getBox();
40084         }
40085         var box = this.el.getBox();
40086         if(this.split){
40087             var sh = this.split.el.getHeight();
40088             box.height += sh;
40089             box.y -= sh;
40090         }
40091         return box;
40092     },
40093     
40094     updateBox : function(box){
40095         if(this.split && !this.collapsed){
40096             var sh = this.split.el.getHeight();
40097             box.height -= sh;
40098             box.y += sh;
40099             this.split.el.setLeft(box.x);
40100             this.split.el.setTop(box.y-sh);
40101             this.split.el.setWidth(box.width);
40102         }
40103         if(this.collapsed){
40104             this.updateBody(box.width, null);
40105         }
40106         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40107     }
40108 });
40109
40110 Roo.bootstrap.layout.East = function(config){
40111     config.region = "east";
40112     config.cursor = "e-resize";
40113     Roo.bootstrap.layout.Split.call(this, config);
40114     if(this.split){
40115         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40116         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40117         this.split.el.addClass("roo-layout-split-h");
40118     }
40119     
40120 };
40121 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40122     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40123     
40124     onRender : function(ctr, pos)
40125     {
40126         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40127         var size = this.config.initialSize || this.config.width;
40128         if(this.el && typeof size != "undefined"){
40129             this.el.setWidth(size);
40130         }
40131     
40132     },
40133     
40134     getBox : function(){
40135         if(this.collapsed){
40136             return this.collapsedEl.getBox();
40137         }
40138         var box = this.el.getBox();
40139         if(this.split){
40140             var sw = this.split.el.getWidth();
40141             box.width += sw;
40142             box.x -= sw;
40143         }
40144         return box;
40145     },
40146
40147     updateBox : function(box){
40148         if(this.split && !this.collapsed){
40149             var sw = this.split.el.getWidth();
40150             box.width -= sw;
40151             this.split.el.setLeft(box.x);
40152             this.split.el.setTop(box.y);
40153             this.split.el.setHeight(box.height);
40154             box.x += sw;
40155         }
40156         if(this.collapsed){
40157             this.updateBody(null, box.height);
40158         }
40159         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40160     }
40161 });
40162
40163 Roo.bootstrap.layout.West = function(config){
40164     config.region = "west";
40165     config.cursor = "w-resize";
40166     
40167     Roo.bootstrap.layout.Split.call(this, config);
40168     if(this.split){
40169         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40170         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40171         this.split.el.addClass("roo-layout-split-h");
40172     }
40173     
40174 };
40175 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40176     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40177     
40178     onRender: function(ctr, pos)
40179     {
40180         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40181         var size = this.config.initialSize || this.config.width;
40182         if(typeof size != "undefined"){
40183             this.el.setWidth(size);
40184         }
40185     },
40186     
40187     getBox : function(){
40188         if(this.collapsed){
40189             return this.collapsedEl.getBox();
40190         }
40191         var box = this.el.getBox();
40192         if (box.width == 0) {
40193             box.width = this.config.width; // kludge?
40194         }
40195         if(this.split){
40196             box.width += this.split.el.getWidth();
40197         }
40198         return box;
40199     },
40200     
40201     updateBox : function(box){
40202         if(this.split && !this.collapsed){
40203             var sw = this.split.el.getWidth();
40204             box.width -= sw;
40205             this.split.el.setLeft(box.x+box.width);
40206             this.split.el.setTop(box.y);
40207             this.split.el.setHeight(box.height);
40208         }
40209         if(this.collapsed){
40210             this.updateBody(null, box.height);
40211         }
40212         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40213     }
40214 });Roo.namespace("Roo.bootstrap.panel");/*
40215  * Based on:
40216  * Ext JS Library 1.1.1
40217  * Copyright(c) 2006-2007, Ext JS, LLC.
40218  *
40219  * Originally Released Under LGPL - original licence link has changed is not relivant.
40220  *
40221  * Fork - LGPL
40222  * <script type="text/javascript">
40223  */
40224 /**
40225  * @class Roo.ContentPanel
40226  * @extends Roo.util.Observable
40227  * A basic ContentPanel element.
40228  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40229  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40230  * @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
40231  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40232  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40233  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40234  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40235  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40236  * @cfg {String} title          The title for this panel
40237  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40238  * @cfg {String} url            Calls {@link #setUrl} with this value
40239  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40240  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40241  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40242  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40243  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40244  * @cfg {Boolean} badges render the badges
40245  * @cfg {String} cls  extra classes to use  
40246  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40247
40248  * @constructor
40249  * Create a new ContentPanel.
40250  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40251  * @param {String/Object} config A string to set only the title or a config object
40252  * @param {String} content (optional) Set the HTML content for this panel
40253  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40254  */
40255 Roo.bootstrap.panel.Content = function( config){
40256     
40257     this.tpl = config.tpl || false;
40258     
40259     var el = config.el;
40260     var content = config.content;
40261
40262     if(config.autoCreate){ // xtype is available if this is called from factory
40263         el = Roo.id();
40264     }
40265     this.el = Roo.get(el);
40266     if(!this.el && config && config.autoCreate){
40267         if(typeof config.autoCreate == "object"){
40268             if(!config.autoCreate.id){
40269                 config.autoCreate.id = config.id||el;
40270             }
40271             this.el = Roo.DomHelper.append(document.body,
40272                         config.autoCreate, true);
40273         }else{
40274             var elcfg =  {
40275                 tag: "div",
40276                 cls: (config.cls || '') +
40277                     (config.background ? ' bg-' + config.background : '') +
40278                     " roo-layout-inactive-content",
40279                 id: config.id||el
40280             };
40281             if (config.iframe) {
40282                 elcfg.cn = [
40283                     {
40284                         tag : 'iframe',
40285                         style : 'border: 0px',
40286                         src : 'about:blank'
40287                     }
40288                 ];
40289             }
40290               
40291             if (config.html) {
40292                 elcfg.html = config.html;
40293                 
40294             }
40295                         
40296             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40297             if (config.iframe) {
40298                 this.iframeEl = this.el.select('iframe',true).first();
40299             }
40300             
40301         }
40302     } 
40303     this.closable = false;
40304     this.loaded = false;
40305     this.active = false;
40306    
40307       
40308     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40309         
40310         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40311         
40312         this.wrapEl = this.el; //this.el.wrap();
40313         var ti = [];
40314         if (config.toolbar.items) {
40315             ti = config.toolbar.items ;
40316             delete config.toolbar.items ;
40317         }
40318         
40319         var nitems = [];
40320         this.toolbar.render(this.wrapEl, 'before');
40321         for(var i =0;i < ti.length;i++) {
40322           //  Roo.log(['add child', items[i]]);
40323             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40324         }
40325         this.toolbar.items = nitems;
40326         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40327         delete config.toolbar;
40328         
40329     }
40330     /*
40331     // xtype created footer. - not sure if will work as we normally have to render first..
40332     if (this.footer && !this.footer.el && this.footer.xtype) {
40333         if (!this.wrapEl) {
40334             this.wrapEl = this.el.wrap();
40335         }
40336     
40337         this.footer.container = this.wrapEl.createChild();
40338          
40339         this.footer = Roo.factory(this.footer, Roo);
40340         
40341     }
40342     */
40343     
40344      if(typeof config == "string"){
40345         this.title = config;
40346     }else{
40347         Roo.apply(this, config);
40348     }
40349     
40350     if(this.resizeEl){
40351         this.resizeEl = Roo.get(this.resizeEl, true);
40352     }else{
40353         this.resizeEl = this.el;
40354     }
40355     // handle view.xtype
40356     
40357  
40358     
40359     
40360     this.addEvents({
40361         /**
40362          * @event activate
40363          * Fires when this panel is activated. 
40364          * @param {Roo.ContentPanel} this
40365          */
40366         "activate" : true,
40367         /**
40368          * @event deactivate
40369          * Fires when this panel is activated. 
40370          * @param {Roo.ContentPanel} this
40371          */
40372         "deactivate" : true,
40373
40374         /**
40375          * @event resize
40376          * Fires when this panel is resized if fitToFrame is true.
40377          * @param {Roo.ContentPanel} this
40378          * @param {Number} width The width after any component adjustments
40379          * @param {Number} height The height after any component adjustments
40380          */
40381         "resize" : true,
40382         
40383          /**
40384          * @event render
40385          * Fires when this tab is created
40386          * @param {Roo.ContentPanel} this
40387          */
40388         "render" : true,
40389         
40390           /**
40391          * @event scroll
40392          * Fires when this content is scrolled
40393          * @param {Roo.ContentPanel} this
40394          * @param {Event} scrollEvent
40395          */
40396         "scroll" : true
40397         
40398         
40399         
40400     });
40401     
40402
40403     
40404     
40405     if(this.autoScroll && !this.iframe){
40406         this.resizeEl.setStyle("overflow", "auto");
40407         this.resizeEl.on('scroll', this.onScroll, this);
40408     } else {
40409         // fix randome scrolling
40410         //this.el.on('scroll', function() {
40411         //    Roo.log('fix random scolling');
40412         //    this.scrollTo('top',0); 
40413         //});
40414     }
40415     content = content || this.content;
40416     if(content){
40417         this.setContent(content);
40418     }
40419     if(config && config.url){
40420         this.setUrl(this.url, this.params, this.loadOnce);
40421     }
40422     
40423     
40424     
40425     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40426     
40427     if (this.view && typeof(this.view.xtype) != 'undefined') {
40428         this.view.el = this.el.appendChild(document.createElement("div"));
40429         this.view = Roo.factory(this.view); 
40430         this.view.render  &&  this.view.render(false, '');  
40431     }
40432     
40433     
40434     this.fireEvent('render', this);
40435 };
40436
40437 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40438     
40439     cls : '',
40440     background : '',
40441     
40442     tabTip : '',
40443     
40444     iframe : false,
40445     iframeEl : false,
40446     
40447     /* Resize Element - use this to work out scroll etc. */
40448     resizeEl : false,
40449     
40450     setRegion : function(region){
40451         this.region = region;
40452         this.setActiveClass(region && !this.background);
40453     },
40454     
40455     
40456     setActiveClass: function(state)
40457     {
40458         if(state){
40459            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40460            this.el.setStyle('position','relative');
40461         }else{
40462            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40463            this.el.setStyle('position', 'absolute');
40464         } 
40465     },
40466     
40467     /**
40468      * Returns the toolbar for this Panel if one was configured. 
40469      * @return {Roo.Toolbar} 
40470      */
40471     getToolbar : function(){
40472         return this.toolbar;
40473     },
40474     
40475     setActiveState : function(active)
40476     {
40477         this.active = active;
40478         this.setActiveClass(active);
40479         if(!active){
40480             if(this.fireEvent("deactivate", this) === false){
40481                 return false;
40482             }
40483             return true;
40484         }
40485         this.fireEvent("activate", this);
40486         return true;
40487     },
40488     /**
40489      * Updates this panel's element (not for iframe)
40490      * @param {String} content The new content
40491      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40492     */
40493     setContent : function(content, loadScripts){
40494         if (this.iframe) {
40495             return;
40496         }
40497         
40498         this.el.update(content, loadScripts);
40499     },
40500
40501     ignoreResize : function(w, h){
40502         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40503             return true;
40504         }else{
40505             this.lastSize = {width: w, height: h};
40506             return false;
40507         }
40508     },
40509     /**
40510      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40511      * @return {Roo.UpdateManager} The UpdateManager
40512      */
40513     getUpdateManager : function(){
40514         if (this.iframe) {
40515             return false;
40516         }
40517         return this.el.getUpdateManager();
40518     },
40519      /**
40520      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40521      * Does not work with IFRAME contents
40522      * @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:
40523 <pre><code>
40524 panel.load({
40525     url: "your-url.php",
40526     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40527     callback: yourFunction,
40528     scope: yourObject, //(optional scope)
40529     discardUrl: false,
40530     nocache: false,
40531     text: "Loading...",
40532     timeout: 30,
40533     scripts: false
40534 });
40535 </code></pre>
40536      
40537      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40538      * 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.
40539      * @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}
40540      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40541      * @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.
40542      * @return {Roo.ContentPanel} this
40543      */
40544     load : function(){
40545         
40546         if (this.iframe) {
40547             return this;
40548         }
40549         
40550         var um = this.el.getUpdateManager();
40551         um.update.apply(um, arguments);
40552         return this;
40553     },
40554
40555
40556     /**
40557      * 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.
40558      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40559      * @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)
40560      * @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)
40561      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40562      */
40563     setUrl : function(url, params, loadOnce){
40564         if (this.iframe) {
40565             this.iframeEl.dom.src = url;
40566             return false;
40567         }
40568         
40569         if(this.refreshDelegate){
40570             this.removeListener("activate", this.refreshDelegate);
40571         }
40572         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40573         this.on("activate", this.refreshDelegate);
40574         return this.el.getUpdateManager();
40575     },
40576     
40577     _handleRefresh : function(url, params, loadOnce){
40578         if(!loadOnce || !this.loaded){
40579             var updater = this.el.getUpdateManager();
40580             updater.update(url, params, this._setLoaded.createDelegate(this));
40581         }
40582     },
40583     
40584     _setLoaded : function(){
40585         this.loaded = true;
40586     }, 
40587     
40588     /**
40589      * Returns this panel's id
40590      * @return {String} 
40591      */
40592     getId : function(){
40593         return this.el.id;
40594     },
40595     
40596     /** 
40597      * Returns this panel's element - used by regiosn to add.
40598      * @return {Roo.Element} 
40599      */
40600     getEl : function(){
40601         return this.wrapEl || this.el;
40602     },
40603     
40604    
40605     
40606     adjustForComponents : function(width, height)
40607     {
40608         //Roo.log('adjustForComponents ');
40609         if(this.resizeEl != this.el){
40610             width -= this.el.getFrameWidth('lr');
40611             height -= this.el.getFrameWidth('tb');
40612         }
40613         if(this.toolbar){
40614             var te = this.toolbar.getEl();
40615             te.setWidth(width);
40616             height -= te.getHeight();
40617         }
40618         if(this.footer){
40619             var te = this.footer.getEl();
40620             te.setWidth(width);
40621             height -= te.getHeight();
40622         }
40623         
40624         
40625         if(this.adjustments){
40626             width += this.adjustments[0];
40627             height += this.adjustments[1];
40628         }
40629         return {"width": width, "height": height};
40630     },
40631     
40632     setSize : function(width, height){
40633         if(this.fitToFrame && !this.ignoreResize(width, height)){
40634             if(this.fitContainer && this.resizeEl != this.el){
40635                 this.el.setSize(width, height);
40636             }
40637             var size = this.adjustForComponents(width, height);
40638             if (this.iframe) {
40639                 this.iframeEl.setSize(width,height);
40640             }
40641             
40642             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40643             this.fireEvent('resize', this, size.width, size.height);
40644             
40645             
40646         }
40647     },
40648     
40649     /**
40650      * Returns this panel's title
40651      * @return {String} 
40652      */
40653     getTitle : function(){
40654         
40655         if (typeof(this.title) != 'object') {
40656             return this.title;
40657         }
40658         
40659         var t = '';
40660         for (var k in this.title) {
40661             if (!this.title.hasOwnProperty(k)) {
40662                 continue;
40663             }
40664             
40665             if (k.indexOf('-') >= 0) {
40666                 var s = k.split('-');
40667                 for (var i = 0; i<s.length; i++) {
40668                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40669                 }
40670             } else {
40671                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40672             }
40673         }
40674         return t;
40675     },
40676     
40677     /**
40678      * Set this panel's title
40679      * @param {String} title
40680      */
40681     setTitle : function(title){
40682         this.title = title;
40683         if(this.region){
40684             this.region.updatePanelTitle(this, title);
40685         }
40686     },
40687     
40688     /**
40689      * Returns true is this panel was configured to be closable
40690      * @return {Boolean} 
40691      */
40692     isClosable : function(){
40693         return this.closable;
40694     },
40695     
40696     beforeSlide : function(){
40697         this.el.clip();
40698         this.resizeEl.clip();
40699     },
40700     
40701     afterSlide : function(){
40702         this.el.unclip();
40703         this.resizeEl.unclip();
40704     },
40705     
40706     /**
40707      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40708      *   Will fail silently if the {@link #setUrl} method has not been called.
40709      *   This does not activate the panel, just updates its content.
40710      */
40711     refresh : function(){
40712         if(this.refreshDelegate){
40713            this.loaded = false;
40714            this.refreshDelegate();
40715         }
40716     },
40717     
40718     /**
40719      * Destroys this panel
40720      */
40721     destroy : function(){
40722         this.el.removeAllListeners();
40723         var tempEl = document.createElement("span");
40724         tempEl.appendChild(this.el.dom);
40725         tempEl.innerHTML = "";
40726         this.el.remove();
40727         this.el = null;
40728     },
40729     
40730     /**
40731      * form - if the content panel contains a form - this is a reference to it.
40732      * @type {Roo.form.Form}
40733      */
40734     form : false,
40735     /**
40736      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40737      *    This contains a reference to it.
40738      * @type {Roo.View}
40739      */
40740     view : false,
40741     
40742       /**
40743      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40744      * <pre><code>
40745
40746 layout.addxtype({
40747        xtype : 'Form',
40748        items: [ .... ]
40749    }
40750 );
40751
40752 </code></pre>
40753      * @param {Object} cfg Xtype definition of item to add.
40754      */
40755     
40756     
40757     getChildContainer: function () {
40758         return this.getEl();
40759     },
40760     
40761     
40762     onScroll : function(e)
40763     {
40764         this.fireEvent('scroll', this, e);
40765     }
40766     
40767     
40768     /*
40769         var  ret = new Roo.factory(cfg);
40770         return ret;
40771         
40772         
40773         // add form..
40774         if (cfg.xtype.match(/^Form$/)) {
40775             
40776             var el;
40777             //if (this.footer) {
40778             //    el = this.footer.container.insertSibling(false, 'before');
40779             //} else {
40780                 el = this.el.createChild();
40781             //}
40782
40783             this.form = new  Roo.form.Form(cfg);
40784             
40785             
40786             if ( this.form.allItems.length) {
40787                 this.form.render(el.dom);
40788             }
40789             return this.form;
40790         }
40791         // should only have one of theses..
40792         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40793             // views.. should not be just added - used named prop 'view''
40794             
40795             cfg.el = this.el.appendChild(document.createElement("div"));
40796             // factory?
40797             
40798             var ret = new Roo.factory(cfg);
40799              
40800              ret.render && ret.render(false, ''); // render blank..
40801             this.view = ret;
40802             return ret;
40803         }
40804         return false;
40805     }
40806     \*/
40807 });
40808  
40809 /**
40810  * @class Roo.bootstrap.panel.Grid
40811  * @extends Roo.bootstrap.panel.Content
40812  * @constructor
40813  * Create a new GridPanel.
40814  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40815  * @param {Object} config A the config object
40816   
40817  */
40818
40819
40820
40821 Roo.bootstrap.panel.Grid = function(config)
40822 {
40823     
40824       
40825     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40826         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40827
40828     config.el = this.wrapper;
40829     //this.el = this.wrapper;
40830     
40831       if (config.container) {
40832         // ctor'ed from a Border/panel.grid
40833         
40834         
40835         this.wrapper.setStyle("overflow", "hidden");
40836         this.wrapper.addClass('roo-grid-container');
40837
40838     }
40839     
40840     
40841     if(config.toolbar){
40842         var tool_el = this.wrapper.createChild();    
40843         this.toolbar = Roo.factory(config.toolbar);
40844         var ti = [];
40845         if (config.toolbar.items) {
40846             ti = config.toolbar.items ;
40847             delete config.toolbar.items ;
40848         }
40849         
40850         var nitems = [];
40851         this.toolbar.render(tool_el);
40852         for(var i =0;i < ti.length;i++) {
40853           //  Roo.log(['add child', items[i]]);
40854             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40855         }
40856         this.toolbar.items = nitems;
40857         
40858         delete config.toolbar;
40859     }
40860     
40861     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40862     config.grid.scrollBody = true;;
40863     config.grid.monitorWindowResize = false; // turn off autosizing
40864     config.grid.autoHeight = false;
40865     config.grid.autoWidth = false;
40866     
40867     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40868     
40869     if (config.background) {
40870         // render grid on panel activation (if panel background)
40871         this.on('activate', function(gp) {
40872             if (!gp.grid.rendered) {
40873                 gp.grid.render(this.wrapper);
40874                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40875             }
40876         });
40877             
40878     } else {
40879         this.grid.render(this.wrapper);
40880         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40881
40882     }
40883     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40884     // ??? needed ??? config.el = this.wrapper;
40885     
40886     
40887     
40888   
40889     // xtype created footer. - not sure if will work as we normally have to render first..
40890     if (this.footer && !this.footer.el && this.footer.xtype) {
40891         
40892         var ctr = this.grid.getView().getFooterPanel(true);
40893         this.footer.dataSource = this.grid.dataSource;
40894         this.footer = Roo.factory(this.footer, Roo);
40895         this.footer.render(ctr);
40896         
40897     }
40898     
40899     
40900     
40901     
40902      
40903 };
40904
40905 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40906     getId : function(){
40907         return this.grid.id;
40908     },
40909     
40910     /**
40911      * Returns the grid for this panel
40912      * @return {Roo.bootstrap.Table} 
40913      */
40914     getGrid : function(){
40915         return this.grid;    
40916     },
40917     
40918     setSize : function(width, height){
40919         if(!this.ignoreResize(width, height)){
40920             var grid = this.grid;
40921             var size = this.adjustForComponents(width, height);
40922             // tfoot is not a footer?
40923           
40924             
40925             var gridel = grid.getGridEl();
40926             gridel.setSize(size.width, size.height);
40927             
40928             var tbd = grid.getGridEl().select('tbody', true).first();
40929             var thd = grid.getGridEl().select('thead',true).first();
40930             var tbf= grid.getGridEl().select('tfoot', true).first();
40931
40932             if (tbf) {
40933                 size.height -= tbf.getHeight();
40934             }
40935             if (thd) {
40936                 size.height -= thd.getHeight();
40937             }
40938             
40939             tbd.setSize(size.width, size.height );
40940             // this is for the account management tab -seems to work there.
40941             var thd = grid.getGridEl().select('thead',true).first();
40942             //if (tbd) {
40943             //    tbd.setSize(size.width, size.height - thd.getHeight());
40944             //}
40945              
40946             grid.autoSize();
40947         }
40948     },
40949      
40950     
40951     
40952     beforeSlide : function(){
40953         this.grid.getView().scroller.clip();
40954     },
40955     
40956     afterSlide : function(){
40957         this.grid.getView().scroller.unclip();
40958     },
40959     
40960     destroy : function(){
40961         this.grid.destroy();
40962         delete this.grid;
40963         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40964     }
40965 });
40966
40967 /**
40968  * @class Roo.bootstrap.panel.Nest
40969  * @extends Roo.bootstrap.panel.Content
40970  * @constructor
40971  * Create a new Panel, that can contain a layout.Border.
40972  * 
40973  * 
40974  * @param {Roo.BorderLayout} layout The layout for this panel
40975  * @param {String/Object} config A string to set only the title or a config object
40976  */
40977 Roo.bootstrap.panel.Nest = function(config)
40978 {
40979     // construct with only one argument..
40980     /* FIXME - implement nicer consturctors
40981     if (layout.layout) {
40982         config = layout;
40983         layout = config.layout;
40984         delete config.layout;
40985     }
40986     if (layout.xtype && !layout.getEl) {
40987         // then layout needs constructing..
40988         layout = Roo.factory(layout, Roo);
40989     }
40990     */
40991     
40992     config.el =  config.layout.getEl();
40993     
40994     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40995     
40996     config.layout.monitorWindowResize = false; // turn off autosizing
40997     this.layout = config.layout;
40998     this.layout.getEl().addClass("roo-layout-nested-layout");
40999     this.layout.parent = this;
41000     
41001     
41002     
41003     
41004 };
41005
41006 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41007
41008     setSize : function(width, height){
41009         if(!this.ignoreResize(width, height)){
41010             var size = this.adjustForComponents(width, height);
41011             var el = this.layout.getEl();
41012             if (size.height < 1) {
41013                 el.setWidth(size.width);   
41014             } else {
41015                 el.setSize(size.width, size.height);
41016             }
41017             var touch = el.dom.offsetWidth;
41018             this.layout.layout();
41019             // ie requires a double layout on the first pass
41020             if(Roo.isIE && !this.initialized){
41021                 this.initialized = true;
41022                 this.layout.layout();
41023             }
41024         }
41025     },
41026     
41027     // activate all subpanels if not currently active..
41028     
41029     setActiveState : function(active){
41030         this.active = active;
41031         this.setActiveClass(active);
41032         
41033         if(!active){
41034             this.fireEvent("deactivate", this);
41035             return;
41036         }
41037         
41038         this.fireEvent("activate", this);
41039         // not sure if this should happen before or after..
41040         if (!this.layout) {
41041             return; // should not happen..
41042         }
41043         var reg = false;
41044         for (var r in this.layout.regions) {
41045             reg = this.layout.getRegion(r);
41046             if (reg.getActivePanel()) {
41047                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41048                 reg.setActivePanel(reg.getActivePanel());
41049                 continue;
41050             }
41051             if (!reg.panels.length) {
41052                 continue;
41053             }
41054             reg.showPanel(reg.getPanel(0));
41055         }
41056         
41057         
41058         
41059         
41060     },
41061     
41062     /**
41063      * Returns the nested BorderLayout for this panel
41064      * @return {Roo.BorderLayout} 
41065      */
41066     getLayout : function(){
41067         return this.layout;
41068     },
41069     
41070      /**
41071      * Adds a xtype elements to the layout of the nested panel
41072      * <pre><code>
41073
41074 panel.addxtype({
41075        xtype : 'ContentPanel',
41076        region: 'west',
41077        items: [ .... ]
41078    }
41079 );
41080
41081 panel.addxtype({
41082         xtype : 'NestedLayoutPanel',
41083         region: 'west',
41084         layout: {
41085            center: { },
41086            west: { }   
41087         },
41088         items : [ ... list of content panels or nested layout panels.. ]
41089    }
41090 );
41091 </code></pre>
41092      * @param {Object} cfg Xtype definition of item to add.
41093      */
41094     addxtype : function(cfg) {
41095         return this.layout.addxtype(cfg);
41096     
41097     }
41098 });/*
41099  * Based on:
41100  * Ext JS Library 1.1.1
41101  * Copyright(c) 2006-2007, Ext JS, LLC.
41102  *
41103  * Originally Released Under LGPL - original licence link has changed is not relivant.
41104  *
41105  * Fork - LGPL
41106  * <script type="text/javascript">
41107  */
41108 /**
41109  * @class Roo.TabPanel
41110  * @extends Roo.util.Observable
41111  * A lightweight tab container.
41112  * <br><br>
41113  * Usage:
41114  * <pre><code>
41115 // basic tabs 1, built from existing content
41116 var tabs = new Roo.TabPanel("tabs1");
41117 tabs.addTab("script", "View Script");
41118 tabs.addTab("markup", "View Markup");
41119 tabs.activate("script");
41120
41121 // more advanced tabs, built from javascript
41122 var jtabs = new Roo.TabPanel("jtabs");
41123 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41124
41125 // set up the UpdateManager
41126 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41127 var updater = tab2.getUpdateManager();
41128 updater.setDefaultUrl("ajax1.htm");
41129 tab2.on('activate', updater.refresh, updater, true);
41130
41131 // Use setUrl for Ajax loading
41132 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41133 tab3.setUrl("ajax2.htm", null, true);
41134
41135 // Disabled tab
41136 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41137 tab4.disable();
41138
41139 jtabs.activate("jtabs-1");
41140  * </code></pre>
41141  * @constructor
41142  * Create a new TabPanel.
41143  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41144  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41145  */
41146 Roo.bootstrap.panel.Tabs = function(config){
41147     /**
41148     * The container element for this TabPanel.
41149     * @type Roo.Element
41150     */
41151     this.el = Roo.get(config.el);
41152     delete config.el;
41153     if(config){
41154         if(typeof config == "boolean"){
41155             this.tabPosition = config ? "bottom" : "top";
41156         }else{
41157             Roo.apply(this, config);
41158         }
41159     }
41160     
41161     if(this.tabPosition == "bottom"){
41162         // if tabs are at the bottom = create the body first.
41163         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41164         this.el.addClass("roo-tabs-bottom");
41165     }
41166     // next create the tabs holders
41167     
41168     if (this.tabPosition == "west"){
41169         
41170         var reg = this.region; // fake it..
41171         while (reg) {
41172             if (!reg.mgr.parent) {
41173                 break;
41174             }
41175             reg = reg.mgr.parent.region;
41176         }
41177         Roo.log("got nest?");
41178         Roo.log(reg);
41179         if (reg.mgr.getRegion('west')) {
41180             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41181             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41182             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41183             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41184             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41185         
41186             
41187         }
41188         
41189         
41190     } else {
41191      
41192         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41193         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41194         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41195         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41196     }
41197     
41198     
41199     if(Roo.isIE){
41200         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41201     }
41202     
41203     // finally - if tabs are at the top, then create the body last..
41204     if(this.tabPosition != "bottom"){
41205         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41206          * @type Roo.Element
41207          */
41208         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41209         this.el.addClass("roo-tabs-top");
41210     }
41211     this.items = [];
41212
41213     this.bodyEl.setStyle("position", "relative");
41214
41215     this.active = null;
41216     this.activateDelegate = this.activate.createDelegate(this);
41217
41218     this.addEvents({
41219         /**
41220          * @event tabchange
41221          * Fires when the active tab changes
41222          * @param {Roo.TabPanel} this
41223          * @param {Roo.TabPanelItem} activePanel The new active tab
41224          */
41225         "tabchange": true,
41226         /**
41227          * @event beforetabchange
41228          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41229          * @param {Roo.TabPanel} this
41230          * @param {Object} e Set cancel to true on this object to cancel the tab change
41231          * @param {Roo.TabPanelItem} tab The tab being changed to
41232          */
41233         "beforetabchange" : true
41234     });
41235
41236     Roo.EventManager.onWindowResize(this.onResize, this);
41237     this.cpad = this.el.getPadding("lr");
41238     this.hiddenCount = 0;
41239
41240
41241     // toolbar on the tabbar support...
41242     if (this.toolbar) {
41243         alert("no toolbar support yet");
41244         this.toolbar  = false;
41245         /*
41246         var tcfg = this.toolbar;
41247         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41248         this.toolbar = new Roo.Toolbar(tcfg);
41249         if (Roo.isSafari) {
41250             var tbl = tcfg.container.child('table', true);
41251             tbl.setAttribute('width', '100%');
41252         }
41253         */
41254         
41255     }
41256    
41257
41258
41259     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41260 };
41261
41262 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41263     /*
41264      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41265      */
41266     tabPosition : "top",
41267     /*
41268      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41269      */
41270     currentTabWidth : 0,
41271     /*
41272      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41273      */
41274     minTabWidth : 40,
41275     /*
41276      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41277      */
41278     maxTabWidth : 250,
41279     /*
41280      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41281      */
41282     preferredTabWidth : 175,
41283     /*
41284      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41285      */
41286     resizeTabs : false,
41287     /*
41288      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41289      */
41290     monitorResize : true,
41291     /*
41292      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41293      */
41294     toolbar : false,  // set by caller..
41295     
41296     region : false, /// set by caller
41297     
41298     disableTooltips : true, // not used yet...
41299
41300     /**
41301      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41302      * @param {String} id The id of the div to use <b>or create</b>
41303      * @param {String} text The text for the tab
41304      * @param {String} content (optional) Content to put in the TabPanelItem body
41305      * @param {Boolean} closable (optional) True to create a close icon on the tab
41306      * @return {Roo.TabPanelItem} The created TabPanelItem
41307      */
41308     addTab : function(id, text, content, closable, tpl)
41309     {
41310         var item = new Roo.bootstrap.panel.TabItem({
41311             panel: this,
41312             id : id,
41313             text : text,
41314             closable : closable,
41315             tpl : tpl
41316         });
41317         this.addTabItem(item);
41318         if(content){
41319             item.setContent(content);
41320         }
41321         return item;
41322     },
41323
41324     /**
41325      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41326      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41327      * @return {Roo.TabPanelItem}
41328      */
41329     getTab : function(id){
41330         return this.items[id];
41331     },
41332
41333     /**
41334      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41335      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41336      */
41337     hideTab : function(id){
41338         var t = this.items[id];
41339         if(!t.isHidden()){
41340            t.setHidden(true);
41341            this.hiddenCount++;
41342            this.autoSizeTabs();
41343         }
41344     },
41345
41346     /**
41347      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41348      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41349      */
41350     unhideTab : function(id){
41351         var t = this.items[id];
41352         if(t.isHidden()){
41353            t.setHidden(false);
41354            this.hiddenCount--;
41355            this.autoSizeTabs();
41356         }
41357     },
41358
41359     /**
41360      * Adds an existing {@link Roo.TabPanelItem}.
41361      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41362      */
41363     addTabItem : function(item)
41364     {
41365         this.items[item.id] = item;
41366         this.items.push(item);
41367         this.autoSizeTabs();
41368       //  if(this.resizeTabs){
41369     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41370   //         this.autoSizeTabs();
41371 //        }else{
41372 //            item.autoSize();
41373        // }
41374     },
41375
41376     /**
41377      * Removes a {@link Roo.TabPanelItem}.
41378      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41379      */
41380     removeTab : function(id){
41381         var items = this.items;
41382         var tab = items[id];
41383         if(!tab) { return; }
41384         var index = items.indexOf(tab);
41385         if(this.active == tab && items.length > 1){
41386             var newTab = this.getNextAvailable(index);
41387             if(newTab) {
41388                 newTab.activate();
41389             }
41390         }
41391         this.stripEl.dom.removeChild(tab.pnode.dom);
41392         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41393             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41394         }
41395         items.splice(index, 1);
41396         delete this.items[tab.id];
41397         tab.fireEvent("close", tab);
41398         tab.purgeListeners();
41399         this.autoSizeTabs();
41400     },
41401
41402     getNextAvailable : function(start){
41403         var items = this.items;
41404         var index = start;
41405         // look for a next tab that will slide over to
41406         // replace the one being removed
41407         while(index < items.length){
41408             var item = items[++index];
41409             if(item && !item.isHidden()){
41410                 return item;
41411             }
41412         }
41413         // if one isn't found select the previous tab (on the left)
41414         index = start;
41415         while(index >= 0){
41416             var item = items[--index];
41417             if(item && !item.isHidden()){
41418                 return item;
41419             }
41420         }
41421         return null;
41422     },
41423
41424     /**
41425      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41426      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41427      */
41428     disableTab : function(id){
41429         var tab = this.items[id];
41430         if(tab && this.active != tab){
41431             tab.disable();
41432         }
41433     },
41434
41435     /**
41436      * Enables a {@link Roo.TabPanelItem} that is disabled.
41437      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41438      */
41439     enableTab : function(id){
41440         var tab = this.items[id];
41441         tab.enable();
41442     },
41443
41444     /**
41445      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41446      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41447      * @return {Roo.TabPanelItem} The TabPanelItem.
41448      */
41449     activate : function(id)
41450     {
41451         //Roo.log('activite:'  + id);
41452         
41453         var tab = this.items[id];
41454         if(!tab){
41455             return null;
41456         }
41457         if(tab == this.active || tab.disabled){
41458             return tab;
41459         }
41460         var e = {};
41461         this.fireEvent("beforetabchange", this, e, tab);
41462         if(e.cancel !== true && !tab.disabled){
41463             if(this.active){
41464                 this.active.hide();
41465             }
41466             this.active = this.items[id];
41467             this.active.show();
41468             this.fireEvent("tabchange", this, this.active);
41469         }
41470         return tab;
41471     },
41472
41473     /**
41474      * Gets the active {@link Roo.TabPanelItem}.
41475      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41476      */
41477     getActiveTab : function(){
41478         return this.active;
41479     },
41480
41481     /**
41482      * Updates the tab body element to fit the height of the container element
41483      * for overflow scrolling
41484      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41485      */
41486     syncHeight : function(targetHeight){
41487         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41488         var bm = this.bodyEl.getMargins();
41489         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41490         this.bodyEl.setHeight(newHeight);
41491         return newHeight;
41492     },
41493
41494     onResize : function(){
41495         if(this.monitorResize){
41496             this.autoSizeTabs();
41497         }
41498     },
41499
41500     /**
41501      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41502      */
41503     beginUpdate : function(){
41504         this.updating = true;
41505     },
41506
41507     /**
41508      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41509      */
41510     endUpdate : function(){
41511         this.updating = false;
41512         this.autoSizeTabs();
41513     },
41514
41515     /**
41516      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41517      */
41518     autoSizeTabs : function()
41519     {
41520         var count = this.items.length;
41521         var vcount = count - this.hiddenCount;
41522         
41523         if (vcount < 2) {
41524             this.stripEl.hide();
41525         } else {
41526             this.stripEl.show();
41527         }
41528         
41529         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41530             return;
41531         }
41532         
41533         
41534         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41535         var availWidth = Math.floor(w / vcount);
41536         var b = this.stripBody;
41537         if(b.getWidth() > w){
41538             var tabs = this.items;
41539             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41540             if(availWidth < this.minTabWidth){
41541                 /*if(!this.sleft){    // incomplete scrolling code
41542                     this.createScrollButtons();
41543                 }
41544                 this.showScroll();
41545                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41546             }
41547         }else{
41548             if(this.currentTabWidth < this.preferredTabWidth){
41549                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41550             }
41551         }
41552     },
41553
41554     /**
41555      * Returns the number of tabs in this TabPanel.
41556      * @return {Number}
41557      */
41558      getCount : function(){
41559          return this.items.length;
41560      },
41561
41562     /**
41563      * Resizes all the tabs to the passed width
41564      * @param {Number} The new width
41565      */
41566     setTabWidth : function(width){
41567         this.currentTabWidth = width;
41568         for(var i = 0, len = this.items.length; i < len; i++) {
41569                 if(!this.items[i].isHidden()) {
41570                 this.items[i].setWidth(width);
41571             }
41572         }
41573     },
41574
41575     /**
41576      * Destroys this TabPanel
41577      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41578      */
41579     destroy : function(removeEl){
41580         Roo.EventManager.removeResizeListener(this.onResize, this);
41581         for(var i = 0, len = this.items.length; i < len; i++){
41582             this.items[i].purgeListeners();
41583         }
41584         if(removeEl === true){
41585             this.el.update("");
41586             this.el.remove();
41587         }
41588     },
41589     
41590     createStrip : function(container)
41591     {
41592         var strip = document.createElement("nav");
41593         strip.className = Roo.bootstrap.version == 4 ?
41594             "navbar-light bg-light" : 
41595             "navbar navbar-default"; //"x-tabs-wrap";
41596         container.appendChild(strip);
41597         return strip;
41598     },
41599     
41600     createStripList : function(strip)
41601     {
41602         // div wrapper for retard IE
41603         // returns the "tr" element.
41604         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41605         //'<div class="x-tabs-strip-wrap">'+
41606           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41607           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41608         return strip.firstChild; //.firstChild.firstChild.firstChild;
41609     },
41610     createBody : function(container)
41611     {
41612         var body = document.createElement("div");
41613         Roo.id(body, "tab-body");
41614         //Roo.fly(body).addClass("x-tabs-body");
41615         Roo.fly(body).addClass("tab-content");
41616         container.appendChild(body);
41617         return body;
41618     },
41619     createItemBody :function(bodyEl, id){
41620         var body = Roo.getDom(id);
41621         if(!body){
41622             body = document.createElement("div");
41623             body.id = id;
41624         }
41625         //Roo.fly(body).addClass("x-tabs-item-body");
41626         Roo.fly(body).addClass("tab-pane");
41627          bodyEl.insertBefore(body, bodyEl.firstChild);
41628         return body;
41629     },
41630     /** @private */
41631     createStripElements :  function(stripEl, text, closable, tpl)
41632     {
41633         var td = document.createElement("li"); // was td..
41634         td.className = 'nav-item';
41635         
41636         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41637         
41638         
41639         stripEl.appendChild(td);
41640         /*if(closable){
41641             td.className = "x-tabs-closable";
41642             if(!this.closeTpl){
41643                 this.closeTpl = new Roo.Template(
41644                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41645                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41646                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41647                 );
41648             }
41649             var el = this.closeTpl.overwrite(td, {"text": text});
41650             var close = el.getElementsByTagName("div")[0];
41651             var inner = el.getElementsByTagName("em")[0];
41652             return {"el": el, "close": close, "inner": inner};
41653         } else {
41654         */
41655         // not sure what this is..
41656 //            if(!this.tabTpl){
41657                 //this.tabTpl = new Roo.Template(
41658                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41659                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41660                 //);
41661 //                this.tabTpl = new Roo.Template(
41662 //                   '<a href="#">' +
41663 //                   '<span unselectable="on"' +
41664 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41665 //                            ' >{text}</span></a>'
41666 //                );
41667 //                
41668 //            }
41669
41670
41671             var template = tpl || this.tabTpl || false;
41672             
41673             if(!template){
41674                 template =  new Roo.Template(
41675                         Roo.bootstrap.version == 4 ? 
41676                             (
41677                                 '<a class="nav-link" href="#" unselectable="on"' +
41678                                      (this.disableTooltips ? '' : ' title="{text}"') +
41679                                      ' >{text}</a>'
41680                             ) : (
41681                                 '<a class="nav-link" href="#">' +
41682                                 '<span unselectable="on"' +
41683                                          (this.disableTooltips ? '' : ' title="{text}"') +
41684                                     ' >{text}</span></a>'
41685                             )
41686                 );
41687             }
41688             
41689             switch (typeof(template)) {
41690                 case 'object' :
41691                     break;
41692                 case 'string' :
41693                     template = new Roo.Template(template);
41694                     break;
41695                 default :
41696                     break;
41697             }
41698             
41699             var el = template.overwrite(td, {"text": text});
41700             
41701             var inner = el.getElementsByTagName("span")[0];
41702             
41703             return {"el": el, "inner": inner};
41704             
41705     }
41706         
41707     
41708 });
41709
41710 /**
41711  * @class Roo.TabPanelItem
41712  * @extends Roo.util.Observable
41713  * Represents an individual item (tab plus body) in a TabPanel.
41714  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41715  * @param {String} id The id of this TabPanelItem
41716  * @param {String} text The text for the tab of this TabPanelItem
41717  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41718  */
41719 Roo.bootstrap.panel.TabItem = function(config){
41720     /**
41721      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41722      * @type Roo.TabPanel
41723      */
41724     this.tabPanel = config.panel;
41725     /**
41726      * The id for this TabPanelItem
41727      * @type String
41728      */
41729     this.id = config.id;
41730     /** @private */
41731     this.disabled = false;
41732     /** @private */
41733     this.text = config.text;
41734     /** @private */
41735     this.loaded = false;
41736     this.closable = config.closable;
41737
41738     /**
41739      * The body element for this TabPanelItem.
41740      * @type Roo.Element
41741      */
41742     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41743     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41744     this.bodyEl.setStyle("display", "block");
41745     this.bodyEl.setStyle("zoom", "1");
41746     //this.hideAction();
41747
41748     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41749     /** @private */
41750     this.el = Roo.get(els.el);
41751     this.inner = Roo.get(els.inner, true);
41752      this.textEl = Roo.bootstrap.version == 4 ?
41753         this.el : Roo.get(this.el.dom.firstChild, true);
41754
41755     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41756     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41757
41758     
41759 //    this.el.on("mousedown", this.onTabMouseDown, this);
41760     this.el.on("click", this.onTabClick, this);
41761     /** @private */
41762     if(config.closable){
41763         var c = Roo.get(els.close, true);
41764         c.dom.title = this.closeText;
41765         c.addClassOnOver("close-over");
41766         c.on("click", this.closeClick, this);
41767      }
41768
41769     this.addEvents({
41770          /**
41771          * @event activate
41772          * Fires when this tab becomes the active tab.
41773          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41774          * @param {Roo.TabPanelItem} this
41775          */
41776         "activate": true,
41777         /**
41778          * @event beforeclose
41779          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41780          * @param {Roo.TabPanelItem} this
41781          * @param {Object} e Set cancel to true on this object to cancel the close.
41782          */
41783         "beforeclose": true,
41784         /**
41785          * @event close
41786          * Fires when this tab is closed.
41787          * @param {Roo.TabPanelItem} this
41788          */
41789          "close": true,
41790         /**
41791          * @event deactivate
41792          * Fires when this tab is no longer the active tab.
41793          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41794          * @param {Roo.TabPanelItem} this
41795          */
41796          "deactivate" : true
41797     });
41798     this.hidden = false;
41799
41800     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41801 };
41802
41803 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41804            {
41805     purgeListeners : function(){
41806        Roo.util.Observable.prototype.purgeListeners.call(this);
41807        this.el.removeAllListeners();
41808     },
41809     /**
41810      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41811      */
41812     show : function(){
41813         this.status_node.addClass("active");
41814         this.showAction();
41815         if(Roo.isOpera){
41816             this.tabPanel.stripWrap.repaint();
41817         }
41818         this.fireEvent("activate", this.tabPanel, this);
41819     },
41820
41821     /**
41822      * Returns true if this tab is the active tab.
41823      * @return {Boolean}
41824      */
41825     isActive : function(){
41826         return this.tabPanel.getActiveTab() == this;
41827     },
41828
41829     /**
41830      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41831      */
41832     hide : function(){
41833         this.status_node.removeClass("active");
41834         this.hideAction();
41835         this.fireEvent("deactivate", this.tabPanel, this);
41836     },
41837
41838     hideAction : function(){
41839         this.bodyEl.hide();
41840         this.bodyEl.setStyle("position", "absolute");
41841         this.bodyEl.setLeft("-20000px");
41842         this.bodyEl.setTop("-20000px");
41843     },
41844
41845     showAction : function(){
41846         this.bodyEl.setStyle("position", "relative");
41847         this.bodyEl.setTop("");
41848         this.bodyEl.setLeft("");
41849         this.bodyEl.show();
41850     },
41851
41852     /**
41853      * Set the tooltip for the tab.
41854      * @param {String} tooltip The tab's tooltip
41855      */
41856     setTooltip : function(text){
41857         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41858             this.textEl.dom.qtip = text;
41859             this.textEl.dom.removeAttribute('title');
41860         }else{
41861             this.textEl.dom.title = text;
41862         }
41863     },
41864
41865     onTabClick : function(e){
41866         e.preventDefault();
41867         this.tabPanel.activate(this.id);
41868     },
41869
41870     onTabMouseDown : function(e){
41871         e.preventDefault();
41872         this.tabPanel.activate(this.id);
41873     },
41874 /*
41875     getWidth : function(){
41876         return this.inner.getWidth();
41877     },
41878
41879     setWidth : function(width){
41880         var iwidth = width - this.linode.getPadding("lr");
41881         this.inner.setWidth(iwidth);
41882         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41883         this.linode.setWidth(width);
41884     },
41885 */
41886     /**
41887      * Show or hide the tab
41888      * @param {Boolean} hidden True to hide or false to show.
41889      */
41890     setHidden : function(hidden){
41891         this.hidden = hidden;
41892         this.linode.setStyle("display", hidden ? "none" : "");
41893     },
41894
41895     /**
41896      * Returns true if this tab is "hidden"
41897      * @return {Boolean}
41898      */
41899     isHidden : function(){
41900         return this.hidden;
41901     },
41902
41903     /**
41904      * Returns the text for this tab
41905      * @return {String}
41906      */
41907     getText : function(){
41908         return this.text;
41909     },
41910     /*
41911     autoSize : function(){
41912         //this.el.beginMeasure();
41913         this.textEl.setWidth(1);
41914         /*
41915          *  #2804 [new] Tabs in Roojs
41916          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41917          */
41918         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41919         //this.el.endMeasure();
41920     //},
41921
41922     /**
41923      * Sets the text for the tab (Note: this also sets the tooltip text)
41924      * @param {String} text The tab's text and tooltip
41925      */
41926     setText : function(text){
41927         this.text = text;
41928         this.textEl.update(text);
41929         this.setTooltip(text);
41930         //if(!this.tabPanel.resizeTabs){
41931         //    this.autoSize();
41932         //}
41933     },
41934     /**
41935      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41936      */
41937     activate : function(){
41938         this.tabPanel.activate(this.id);
41939     },
41940
41941     /**
41942      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41943      */
41944     disable : function(){
41945         if(this.tabPanel.active != this){
41946             this.disabled = true;
41947             this.status_node.addClass("disabled");
41948         }
41949     },
41950
41951     /**
41952      * Enables this TabPanelItem if it was previously disabled.
41953      */
41954     enable : function(){
41955         this.disabled = false;
41956         this.status_node.removeClass("disabled");
41957     },
41958
41959     /**
41960      * Sets the content for this TabPanelItem.
41961      * @param {String} content The content
41962      * @param {Boolean} loadScripts true to look for and load scripts
41963      */
41964     setContent : function(content, loadScripts){
41965         this.bodyEl.update(content, loadScripts);
41966     },
41967
41968     /**
41969      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41970      * @return {Roo.UpdateManager} The UpdateManager
41971      */
41972     getUpdateManager : function(){
41973         return this.bodyEl.getUpdateManager();
41974     },
41975
41976     /**
41977      * Set a URL to be used to load the content for this TabPanelItem.
41978      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41979      * @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)
41980      * @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)
41981      * @return {Roo.UpdateManager} The UpdateManager
41982      */
41983     setUrl : function(url, params, loadOnce){
41984         if(this.refreshDelegate){
41985             this.un('activate', this.refreshDelegate);
41986         }
41987         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41988         this.on("activate", this.refreshDelegate);
41989         return this.bodyEl.getUpdateManager();
41990     },
41991
41992     /** @private */
41993     _handleRefresh : function(url, params, loadOnce){
41994         if(!loadOnce || !this.loaded){
41995             var updater = this.bodyEl.getUpdateManager();
41996             updater.update(url, params, this._setLoaded.createDelegate(this));
41997         }
41998     },
41999
42000     /**
42001      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42002      *   Will fail silently if the setUrl method has not been called.
42003      *   This does not activate the panel, just updates its content.
42004      */
42005     refresh : function(){
42006         if(this.refreshDelegate){
42007            this.loaded = false;
42008            this.refreshDelegate();
42009         }
42010     },
42011
42012     /** @private */
42013     _setLoaded : function(){
42014         this.loaded = true;
42015     },
42016
42017     /** @private */
42018     closeClick : function(e){
42019         var o = {};
42020         e.stopEvent();
42021         this.fireEvent("beforeclose", this, o);
42022         if(o.cancel !== true){
42023             this.tabPanel.removeTab(this.id);
42024         }
42025     },
42026     /**
42027      * The text displayed in the tooltip for the close icon.
42028      * @type String
42029      */
42030     closeText : "Close this tab"
42031 });
42032 /**
42033 *    This script refer to:
42034 *    Title: International Telephone Input
42035 *    Author: Jack O'Connor
42036 *    Code version:  v12.1.12
42037 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42038 **/
42039
42040 Roo.bootstrap.PhoneInputData = function() {
42041     var d = [
42042       [
42043         "Afghanistan (‫افغانستان‬‎)",
42044         "af",
42045         "93"
42046       ],
42047       [
42048         "Albania (Shqipëri)",
42049         "al",
42050         "355"
42051       ],
42052       [
42053         "Algeria (‫الجزائر‬‎)",
42054         "dz",
42055         "213"
42056       ],
42057       [
42058         "American Samoa",
42059         "as",
42060         "1684"
42061       ],
42062       [
42063         "Andorra",
42064         "ad",
42065         "376"
42066       ],
42067       [
42068         "Angola",
42069         "ao",
42070         "244"
42071       ],
42072       [
42073         "Anguilla",
42074         "ai",
42075         "1264"
42076       ],
42077       [
42078         "Antigua and Barbuda",
42079         "ag",
42080         "1268"
42081       ],
42082       [
42083         "Argentina",
42084         "ar",
42085         "54"
42086       ],
42087       [
42088         "Armenia (Հայաստան)",
42089         "am",
42090         "374"
42091       ],
42092       [
42093         "Aruba",
42094         "aw",
42095         "297"
42096       ],
42097       [
42098         "Australia",
42099         "au",
42100         "61",
42101         0
42102       ],
42103       [
42104         "Austria (Österreich)",
42105         "at",
42106         "43"
42107       ],
42108       [
42109         "Azerbaijan (Azərbaycan)",
42110         "az",
42111         "994"
42112       ],
42113       [
42114         "Bahamas",
42115         "bs",
42116         "1242"
42117       ],
42118       [
42119         "Bahrain (‫البحرين‬‎)",
42120         "bh",
42121         "973"
42122       ],
42123       [
42124         "Bangladesh (বাংলাদেশ)",
42125         "bd",
42126         "880"
42127       ],
42128       [
42129         "Barbados",
42130         "bb",
42131         "1246"
42132       ],
42133       [
42134         "Belarus (Беларусь)",
42135         "by",
42136         "375"
42137       ],
42138       [
42139         "Belgium (België)",
42140         "be",
42141         "32"
42142       ],
42143       [
42144         "Belize",
42145         "bz",
42146         "501"
42147       ],
42148       [
42149         "Benin (Bénin)",
42150         "bj",
42151         "229"
42152       ],
42153       [
42154         "Bermuda",
42155         "bm",
42156         "1441"
42157       ],
42158       [
42159         "Bhutan (འབྲུག)",
42160         "bt",
42161         "975"
42162       ],
42163       [
42164         "Bolivia",
42165         "bo",
42166         "591"
42167       ],
42168       [
42169         "Bosnia and Herzegovina (Босна и Херцеговина)",
42170         "ba",
42171         "387"
42172       ],
42173       [
42174         "Botswana",
42175         "bw",
42176         "267"
42177       ],
42178       [
42179         "Brazil (Brasil)",
42180         "br",
42181         "55"
42182       ],
42183       [
42184         "British Indian Ocean Territory",
42185         "io",
42186         "246"
42187       ],
42188       [
42189         "British Virgin Islands",
42190         "vg",
42191         "1284"
42192       ],
42193       [
42194         "Brunei",
42195         "bn",
42196         "673"
42197       ],
42198       [
42199         "Bulgaria (България)",
42200         "bg",
42201         "359"
42202       ],
42203       [
42204         "Burkina Faso",
42205         "bf",
42206         "226"
42207       ],
42208       [
42209         "Burundi (Uburundi)",
42210         "bi",
42211         "257"
42212       ],
42213       [
42214         "Cambodia (កម្ពុជា)",
42215         "kh",
42216         "855"
42217       ],
42218       [
42219         "Cameroon (Cameroun)",
42220         "cm",
42221         "237"
42222       ],
42223       [
42224         "Canada",
42225         "ca",
42226         "1",
42227         1,
42228         ["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"]
42229       ],
42230       [
42231         "Cape Verde (Kabu Verdi)",
42232         "cv",
42233         "238"
42234       ],
42235       [
42236         "Caribbean Netherlands",
42237         "bq",
42238         "599",
42239         1
42240       ],
42241       [
42242         "Cayman Islands",
42243         "ky",
42244         "1345"
42245       ],
42246       [
42247         "Central African Republic (République centrafricaine)",
42248         "cf",
42249         "236"
42250       ],
42251       [
42252         "Chad (Tchad)",
42253         "td",
42254         "235"
42255       ],
42256       [
42257         "Chile",
42258         "cl",
42259         "56"
42260       ],
42261       [
42262         "China (中国)",
42263         "cn",
42264         "86"
42265       ],
42266       [
42267         "Christmas Island",
42268         "cx",
42269         "61",
42270         2
42271       ],
42272       [
42273         "Cocos (Keeling) Islands",
42274         "cc",
42275         "61",
42276         1
42277       ],
42278       [
42279         "Colombia",
42280         "co",
42281         "57"
42282       ],
42283       [
42284         "Comoros (‫جزر القمر‬‎)",
42285         "km",
42286         "269"
42287       ],
42288       [
42289         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42290         "cd",
42291         "243"
42292       ],
42293       [
42294         "Congo (Republic) (Congo-Brazzaville)",
42295         "cg",
42296         "242"
42297       ],
42298       [
42299         "Cook Islands",
42300         "ck",
42301         "682"
42302       ],
42303       [
42304         "Costa Rica",
42305         "cr",
42306         "506"
42307       ],
42308       [
42309         "Côte d’Ivoire",
42310         "ci",
42311         "225"
42312       ],
42313       [
42314         "Croatia (Hrvatska)",
42315         "hr",
42316         "385"
42317       ],
42318       [
42319         "Cuba",
42320         "cu",
42321         "53"
42322       ],
42323       [
42324         "Curaçao",
42325         "cw",
42326         "599",
42327         0
42328       ],
42329       [
42330         "Cyprus (Κύπρος)",
42331         "cy",
42332         "357"
42333       ],
42334       [
42335         "Czech Republic (Česká republika)",
42336         "cz",
42337         "420"
42338       ],
42339       [
42340         "Denmark (Danmark)",
42341         "dk",
42342         "45"
42343       ],
42344       [
42345         "Djibouti",
42346         "dj",
42347         "253"
42348       ],
42349       [
42350         "Dominica",
42351         "dm",
42352         "1767"
42353       ],
42354       [
42355         "Dominican Republic (República Dominicana)",
42356         "do",
42357         "1",
42358         2,
42359         ["809", "829", "849"]
42360       ],
42361       [
42362         "Ecuador",
42363         "ec",
42364         "593"
42365       ],
42366       [
42367         "Egypt (‫مصر‬‎)",
42368         "eg",
42369         "20"
42370       ],
42371       [
42372         "El Salvador",
42373         "sv",
42374         "503"
42375       ],
42376       [
42377         "Equatorial Guinea (Guinea Ecuatorial)",
42378         "gq",
42379         "240"
42380       ],
42381       [
42382         "Eritrea",
42383         "er",
42384         "291"
42385       ],
42386       [
42387         "Estonia (Eesti)",
42388         "ee",
42389         "372"
42390       ],
42391       [
42392         "Ethiopia",
42393         "et",
42394         "251"
42395       ],
42396       [
42397         "Falkland Islands (Islas Malvinas)",
42398         "fk",
42399         "500"
42400       ],
42401       [
42402         "Faroe Islands (Føroyar)",
42403         "fo",
42404         "298"
42405       ],
42406       [
42407         "Fiji",
42408         "fj",
42409         "679"
42410       ],
42411       [
42412         "Finland (Suomi)",
42413         "fi",
42414         "358",
42415         0
42416       ],
42417       [
42418         "France",
42419         "fr",
42420         "33"
42421       ],
42422       [
42423         "French Guiana (Guyane française)",
42424         "gf",
42425         "594"
42426       ],
42427       [
42428         "French Polynesia (Polynésie française)",
42429         "pf",
42430         "689"
42431       ],
42432       [
42433         "Gabon",
42434         "ga",
42435         "241"
42436       ],
42437       [
42438         "Gambia",
42439         "gm",
42440         "220"
42441       ],
42442       [
42443         "Georgia (საქართველო)",
42444         "ge",
42445         "995"
42446       ],
42447       [
42448         "Germany (Deutschland)",
42449         "de",
42450         "49"
42451       ],
42452       [
42453         "Ghana (Gaana)",
42454         "gh",
42455         "233"
42456       ],
42457       [
42458         "Gibraltar",
42459         "gi",
42460         "350"
42461       ],
42462       [
42463         "Greece (Ελλάδα)",
42464         "gr",
42465         "30"
42466       ],
42467       [
42468         "Greenland (Kalaallit Nunaat)",
42469         "gl",
42470         "299"
42471       ],
42472       [
42473         "Grenada",
42474         "gd",
42475         "1473"
42476       ],
42477       [
42478         "Guadeloupe",
42479         "gp",
42480         "590",
42481         0
42482       ],
42483       [
42484         "Guam",
42485         "gu",
42486         "1671"
42487       ],
42488       [
42489         "Guatemala",
42490         "gt",
42491         "502"
42492       ],
42493       [
42494         "Guernsey",
42495         "gg",
42496         "44",
42497         1
42498       ],
42499       [
42500         "Guinea (Guinée)",
42501         "gn",
42502         "224"
42503       ],
42504       [
42505         "Guinea-Bissau (Guiné Bissau)",
42506         "gw",
42507         "245"
42508       ],
42509       [
42510         "Guyana",
42511         "gy",
42512         "592"
42513       ],
42514       [
42515         "Haiti",
42516         "ht",
42517         "509"
42518       ],
42519       [
42520         "Honduras",
42521         "hn",
42522         "504"
42523       ],
42524       [
42525         "Hong Kong (香港)",
42526         "hk",
42527         "852"
42528       ],
42529       [
42530         "Hungary (Magyarország)",
42531         "hu",
42532         "36"
42533       ],
42534       [
42535         "Iceland (Ísland)",
42536         "is",
42537         "354"
42538       ],
42539       [
42540         "India (भारत)",
42541         "in",
42542         "91"
42543       ],
42544       [
42545         "Indonesia",
42546         "id",
42547         "62"
42548       ],
42549       [
42550         "Iran (‫ایران‬‎)",
42551         "ir",
42552         "98"
42553       ],
42554       [
42555         "Iraq (‫العراق‬‎)",
42556         "iq",
42557         "964"
42558       ],
42559       [
42560         "Ireland",
42561         "ie",
42562         "353"
42563       ],
42564       [
42565         "Isle of Man",
42566         "im",
42567         "44",
42568         2
42569       ],
42570       [
42571         "Israel (‫ישראל‬‎)",
42572         "il",
42573         "972"
42574       ],
42575       [
42576         "Italy (Italia)",
42577         "it",
42578         "39",
42579         0
42580       ],
42581       [
42582         "Jamaica",
42583         "jm",
42584         "1876"
42585       ],
42586       [
42587         "Japan (日本)",
42588         "jp",
42589         "81"
42590       ],
42591       [
42592         "Jersey",
42593         "je",
42594         "44",
42595         3
42596       ],
42597       [
42598         "Jordan (‫الأردن‬‎)",
42599         "jo",
42600         "962"
42601       ],
42602       [
42603         "Kazakhstan (Казахстан)",
42604         "kz",
42605         "7",
42606         1
42607       ],
42608       [
42609         "Kenya",
42610         "ke",
42611         "254"
42612       ],
42613       [
42614         "Kiribati",
42615         "ki",
42616         "686"
42617       ],
42618       [
42619         "Kosovo",
42620         "xk",
42621         "383"
42622       ],
42623       [
42624         "Kuwait (‫الكويت‬‎)",
42625         "kw",
42626         "965"
42627       ],
42628       [
42629         "Kyrgyzstan (Кыргызстан)",
42630         "kg",
42631         "996"
42632       ],
42633       [
42634         "Laos (ລາວ)",
42635         "la",
42636         "856"
42637       ],
42638       [
42639         "Latvia (Latvija)",
42640         "lv",
42641         "371"
42642       ],
42643       [
42644         "Lebanon (‫لبنان‬‎)",
42645         "lb",
42646         "961"
42647       ],
42648       [
42649         "Lesotho",
42650         "ls",
42651         "266"
42652       ],
42653       [
42654         "Liberia",
42655         "lr",
42656         "231"
42657       ],
42658       [
42659         "Libya (‫ليبيا‬‎)",
42660         "ly",
42661         "218"
42662       ],
42663       [
42664         "Liechtenstein",
42665         "li",
42666         "423"
42667       ],
42668       [
42669         "Lithuania (Lietuva)",
42670         "lt",
42671         "370"
42672       ],
42673       [
42674         "Luxembourg",
42675         "lu",
42676         "352"
42677       ],
42678       [
42679         "Macau (澳門)",
42680         "mo",
42681         "853"
42682       ],
42683       [
42684         "Macedonia (FYROM) (Македонија)",
42685         "mk",
42686         "389"
42687       ],
42688       [
42689         "Madagascar (Madagasikara)",
42690         "mg",
42691         "261"
42692       ],
42693       [
42694         "Malawi",
42695         "mw",
42696         "265"
42697       ],
42698       [
42699         "Malaysia",
42700         "my",
42701         "60"
42702       ],
42703       [
42704         "Maldives",
42705         "mv",
42706         "960"
42707       ],
42708       [
42709         "Mali",
42710         "ml",
42711         "223"
42712       ],
42713       [
42714         "Malta",
42715         "mt",
42716         "356"
42717       ],
42718       [
42719         "Marshall Islands",
42720         "mh",
42721         "692"
42722       ],
42723       [
42724         "Martinique",
42725         "mq",
42726         "596"
42727       ],
42728       [
42729         "Mauritania (‫موريتانيا‬‎)",
42730         "mr",
42731         "222"
42732       ],
42733       [
42734         "Mauritius (Moris)",
42735         "mu",
42736         "230"
42737       ],
42738       [
42739         "Mayotte",
42740         "yt",
42741         "262",
42742         1
42743       ],
42744       [
42745         "Mexico (México)",
42746         "mx",
42747         "52"
42748       ],
42749       [
42750         "Micronesia",
42751         "fm",
42752         "691"
42753       ],
42754       [
42755         "Moldova (Republica Moldova)",
42756         "md",
42757         "373"
42758       ],
42759       [
42760         "Monaco",
42761         "mc",
42762         "377"
42763       ],
42764       [
42765         "Mongolia (Монгол)",
42766         "mn",
42767         "976"
42768       ],
42769       [
42770         "Montenegro (Crna Gora)",
42771         "me",
42772         "382"
42773       ],
42774       [
42775         "Montserrat",
42776         "ms",
42777         "1664"
42778       ],
42779       [
42780         "Morocco (‫المغرب‬‎)",
42781         "ma",
42782         "212",
42783         0
42784       ],
42785       [
42786         "Mozambique (Moçambique)",
42787         "mz",
42788         "258"
42789       ],
42790       [
42791         "Myanmar (Burma) (မြန်မာ)",
42792         "mm",
42793         "95"
42794       ],
42795       [
42796         "Namibia (Namibië)",
42797         "na",
42798         "264"
42799       ],
42800       [
42801         "Nauru",
42802         "nr",
42803         "674"
42804       ],
42805       [
42806         "Nepal (नेपाल)",
42807         "np",
42808         "977"
42809       ],
42810       [
42811         "Netherlands (Nederland)",
42812         "nl",
42813         "31"
42814       ],
42815       [
42816         "New Caledonia (Nouvelle-Calédonie)",
42817         "nc",
42818         "687"
42819       ],
42820       [
42821         "New Zealand",
42822         "nz",
42823         "64"
42824       ],
42825       [
42826         "Nicaragua",
42827         "ni",
42828         "505"
42829       ],
42830       [
42831         "Niger (Nijar)",
42832         "ne",
42833         "227"
42834       ],
42835       [
42836         "Nigeria",
42837         "ng",
42838         "234"
42839       ],
42840       [
42841         "Niue",
42842         "nu",
42843         "683"
42844       ],
42845       [
42846         "Norfolk Island",
42847         "nf",
42848         "672"
42849       ],
42850       [
42851         "North Korea (조선 민주주의 인민 공화국)",
42852         "kp",
42853         "850"
42854       ],
42855       [
42856         "Northern Mariana Islands",
42857         "mp",
42858         "1670"
42859       ],
42860       [
42861         "Norway (Norge)",
42862         "no",
42863         "47",
42864         0
42865       ],
42866       [
42867         "Oman (‫عُمان‬‎)",
42868         "om",
42869         "968"
42870       ],
42871       [
42872         "Pakistan (‫پاکستان‬‎)",
42873         "pk",
42874         "92"
42875       ],
42876       [
42877         "Palau",
42878         "pw",
42879         "680"
42880       ],
42881       [
42882         "Palestine (‫فلسطين‬‎)",
42883         "ps",
42884         "970"
42885       ],
42886       [
42887         "Panama (Panamá)",
42888         "pa",
42889         "507"
42890       ],
42891       [
42892         "Papua New Guinea",
42893         "pg",
42894         "675"
42895       ],
42896       [
42897         "Paraguay",
42898         "py",
42899         "595"
42900       ],
42901       [
42902         "Peru (Perú)",
42903         "pe",
42904         "51"
42905       ],
42906       [
42907         "Philippines",
42908         "ph",
42909         "63"
42910       ],
42911       [
42912         "Poland (Polska)",
42913         "pl",
42914         "48"
42915       ],
42916       [
42917         "Portugal",
42918         "pt",
42919         "351"
42920       ],
42921       [
42922         "Puerto Rico",
42923         "pr",
42924         "1",
42925         3,
42926         ["787", "939"]
42927       ],
42928       [
42929         "Qatar (‫قطر‬‎)",
42930         "qa",
42931         "974"
42932       ],
42933       [
42934         "Réunion (La Réunion)",
42935         "re",
42936         "262",
42937         0
42938       ],
42939       [
42940         "Romania (România)",
42941         "ro",
42942         "40"
42943       ],
42944       [
42945         "Russia (Россия)",
42946         "ru",
42947         "7",
42948         0
42949       ],
42950       [
42951         "Rwanda",
42952         "rw",
42953         "250"
42954       ],
42955       [
42956         "Saint Barthélemy",
42957         "bl",
42958         "590",
42959         1
42960       ],
42961       [
42962         "Saint Helena",
42963         "sh",
42964         "290"
42965       ],
42966       [
42967         "Saint Kitts and Nevis",
42968         "kn",
42969         "1869"
42970       ],
42971       [
42972         "Saint Lucia",
42973         "lc",
42974         "1758"
42975       ],
42976       [
42977         "Saint Martin (Saint-Martin (partie française))",
42978         "mf",
42979         "590",
42980         2
42981       ],
42982       [
42983         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42984         "pm",
42985         "508"
42986       ],
42987       [
42988         "Saint Vincent and the Grenadines",
42989         "vc",
42990         "1784"
42991       ],
42992       [
42993         "Samoa",
42994         "ws",
42995         "685"
42996       ],
42997       [
42998         "San Marino",
42999         "sm",
43000         "378"
43001       ],
43002       [
43003         "São Tomé and Príncipe (São Tomé e Príncipe)",
43004         "st",
43005         "239"
43006       ],
43007       [
43008         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43009         "sa",
43010         "966"
43011       ],
43012       [
43013         "Senegal (Sénégal)",
43014         "sn",
43015         "221"
43016       ],
43017       [
43018         "Serbia (Србија)",
43019         "rs",
43020         "381"
43021       ],
43022       [
43023         "Seychelles",
43024         "sc",
43025         "248"
43026       ],
43027       [
43028         "Sierra Leone",
43029         "sl",
43030         "232"
43031       ],
43032       [
43033         "Singapore",
43034         "sg",
43035         "65"
43036       ],
43037       [
43038         "Sint Maarten",
43039         "sx",
43040         "1721"
43041       ],
43042       [
43043         "Slovakia (Slovensko)",
43044         "sk",
43045         "421"
43046       ],
43047       [
43048         "Slovenia (Slovenija)",
43049         "si",
43050         "386"
43051       ],
43052       [
43053         "Solomon Islands",
43054         "sb",
43055         "677"
43056       ],
43057       [
43058         "Somalia (Soomaaliya)",
43059         "so",
43060         "252"
43061       ],
43062       [
43063         "South Africa",
43064         "za",
43065         "27"
43066       ],
43067       [
43068         "South Korea (대한민국)",
43069         "kr",
43070         "82"
43071       ],
43072       [
43073         "South Sudan (‫جنوب السودان‬‎)",
43074         "ss",
43075         "211"
43076       ],
43077       [
43078         "Spain (España)",
43079         "es",
43080         "34"
43081       ],
43082       [
43083         "Sri Lanka (ශ්‍රී ලංකාව)",
43084         "lk",
43085         "94"
43086       ],
43087       [
43088         "Sudan (‫السودان‬‎)",
43089         "sd",
43090         "249"
43091       ],
43092       [
43093         "Suriname",
43094         "sr",
43095         "597"
43096       ],
43097       [
43098         "Svalbard and Jan Mayen",
43099         "sj",
43100         "47",
43101         1
43102       ],
43103       [
43104         "Swaziland",
43105         "sz",
43106         "268"
43107       ],
43108       [
43109         "Sweden (Sverige)",
43110         "se",
43111         "46"
43112       ],
43113       [
43114         "Switzerland (Schweiz)",
43115         "ch",
43116         "41"
43117       ],
43118       [
43119         "Syria (‫سوريا‬‎)",
43120         "sy",
43121         "963"
43122       ],
43123       [
43124         "Taiwan (台灣)",
43125         "tw",
43126         "886"
43127       ],
43128       [
43129         "Tajikistan",
43130         "tj",
43131         "992"
43132       ],
43133       [
43134         "Tanzania",
43135         "tz",
43136         "255"
43137       ],
43138       [
43139         "Thailand (ไทย)",
43140         "th",
43141         "66"
43142       ],
43143       [
43144         "Timor-Leste",
43145         "tl",
43146         "670"
43147       ],
43148       [
43149         "Togo",
43150         "tg",
43151         "228"
43152       ],
43153       [
43154         "Tokelau",
43155         "tk",
43156         "690"
43157       ],
43158       [
43159         "Tonga",
43160         "to",
43161         "676"
43162       ],
43163       [
43164         "Trinidad and Tobago",
43165         "tt",
43166         "1868"
43167       ],
43168       [
43169         "Tunisia (‫تونس‬‎)",
43170         "tn",
43171         "216"
43172       ],
43173       [
43174         "Turkey (Türkiye)",
43175         "tr",
43176         "90"
43177       ],
43178       [
43179         "Turkmenistan",
43180         "tm",
43181         "993"
43182       ],
43183       [
43184         "Turks and Caicos Islands",
43185         "tc",
43186         "1649"
43187       ],
43188       [
43189         "Tuvalu",
43190         "tv",
43191         "688"
43192       ],
43193       [
43194         "U.S. Virgin Islands",
43195         "vi",
43196         "1340"
43197       ],
43198       [
43199         "Uganda",
43200         "ug",
43201         "256"
43202       ],
43203       [
43204         "Ukraine (Україна)",
43205         "ua",
43206         "380"
43207       ],
43208       [
43209         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43210         "ae",
43211         "971"
43212       ],
43213       [
43214         "United Kingdom",
43215         "gb",
43216         "44",
43217         0
43218       ],
43219       [
43220         "United States",
43221         "us",
43222         "1",
43223         0
43224       ],
43225       [
43226         "Uruguay",
43227         "uy",
43228         "598"
43229       ],
43230       [
43231         "Uzbekistan (Oʻzbekiston)",
43232         "uz",
43233         "998"
43234       ],
43235       [
43236         "Vanuatu",
43237         "vu",
43238         "678"
43239       ],
43240       [
43241         "Vatican City (Città del Vaticano)",
43242         "va",
43243         "39",
43244         1
43245       ],
43246       [
43247         "Venezuela",
43248         "ve",
43249         "58"
43250       ],
43251       [
43252         "Vietnam (Việt Nam)",
43253         "vn",
43254         "84"
43255       ],
43256       [
43257         "Wallis and Futuna (Wallis-et-Futuna)",
43258         "wf",
43259         "681"
43260       ],
43261       [
43262         "Western Sahara (‫الصحراء الغربية‬‎)",
43263         "eh",
43264         "212",
43265         1
43266       ],
43267       [
43268         "Yemen (‫اليمن‬‎)",
43269         "ye",
43270         "967"
43271       ],
43272       [
43273         "Zambia",
43274         "zm",
43275         "260"
43276       ],
43277       [
43278         "Zimbabwe",
43279         "zw",
43280         "263"
43281       ],
43282       [
43283         "Åland Islands",
43284         "ax",
43285         "358",
43286         1
43287       ]
43288   ];
43289   
43290   return d;
43291 }/**
43292 *    This script refer to:
43293 *    Title: International Telephone Input
43294 *    Author: Jack O'Connor
43295 *    Code version:  v12.1.12
43296 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43297 **/
43298
43299 /**
43300  * @class Roo.bootstrap.PhoneInput
43301  * @extends Roo.bootstrap.TriggerField
43302  * An input with International dial-code selection
43303  
43304  * @cfg {String} defaultDialCode default '+852'
43305  * @cfg {Array} preferedCountries default []
43306   
43307  * @constructor
43308  * Create a new PhoneInput.
43309  * @param {Object} config Configuration options
43310  */
43311
43312 Roo.bootstrap.PhoneInput = function(config) {
43313     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43314 };
43315
43316 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
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     store : false,
43934     
43935     getAutoCreate : function()
43936     {
43937         var align = this.labelAlign || this.parentLabelAlign();
43938         
43939         var id = Roo.id();
43940
43941         var cfg = {
43942             cls: 'form-group',
43943             cn: []
43944         };
43945
43946         var input =  {
43947             tag: 'input',
43948             id : id,
43949             cls : 'form-control roo-money-amount-input',
43950             autocomplete: 'new-password'
43951         };
43952         
43953         var hiddenInput = {
43954             tag: 'input',
43955             type: 'hidden',
43956             id: Roo.id(),
43957             cls: 'hidden-number-input'
43958         };
43959         
43960         if(this.max_length) {
43961             input.maxlength = this.max_length; 
43962         }
43963         
43964         if (this.name) {
43965             hiddenInput.name = this.name;
43966         }
43967
43968         if (this.disabled) {
43969             input.disabled = true;
43970         }
43971
43972         var clg = 12 - this.inputlg;
43973         var cmd = 12 - this.inputmd;
43974         var csm = 12 - this.inputsm;
43975         var cxs = 12 - this.inputxs;
43976         
43977         var container = {
43978             tag : 'div',
43979             cls : 'row roo-money-field',
43980             cn : [
43981                 {
43982                     tag : 'div',
43983                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43984                     cn : [
43985                         {
43986                             tag : 'div',
43987                             cls: 'roo-select2-container input-group',
43988                             cn: [
43989                                 {
43990                                     tag : 'input',
43991                                     cls : 'form-control roo-money-currency-input',
43992                                     autocomplete: 'new-password',
43993                                     readOnly : 1,
43994                                     name : this.currencyName
43995                                 },
43996                                 {
43997                                     tag :'span',
43998                                     cls : 'input-group-addon',
43999                                     cn : [
44000                                         {
44001                                             tag: 'span',
44002                                             cls: 'caret'
44003                                         }
44004                                     ]
44005                                 }
44006                             ]
44007                         }
44008                     ]
44009                 },
44010                 {
44011                     tag : 'div',
44012                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44013                     cn : [
44014                         {
44015                             tag: 'div',
44016                             cls: this.hasFeedback ? 'has-feedback' : '',
44017                             cn: [
44018                                 input
44019                             ]
44020                         }
44021                     ]
44022                 }
44023             ]
44024             
44025         };
44026         
44027         if (this.fieldLabel.length) {
44028             var indicator = {
44029                 tag: 'i',
44030                 tooltip: 'This field is required'
44031             };
44032
44033             var label = {
44034                 tag: 'label',
44035                 'for':  id,
44036                 cls: 'control-label',
44037                 cn: []
44038             };
44039
44040             var label_text = {
44041                 tag: 'span',
44042                 html: this.fieldLabel
44043             };
44044
44045             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44046             label.cn = [
44047                 indicator,
44048                 label_text
44049             ];
44050
44051             if(this.indicatorpos == 'right') {
44052                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44053                 label.cn = [
44054                     label_text,
44055                     indicator
44056                 ];
44057             }
44058
44059             if(align == 'left') {
44060                 container = {
44061                     tag: 'div',
44062                     cn: [
44063                         container
44064                     ]
44065                 };
44066
44067                 if(this.labelWidth > 12){
44068                     label.style = "width: " + this.labelWidth + 'px';
44069                 }
44070                 if(this.labelWidth < 13 && this.labelmd == 0){
44071                     this.labelmd = this.labelWidth;
44072                 }
44073                 if(this.labellg > 0){
44074                     label.cls += ' col-lg-' + this.labellg;
44075                     input.cls += ' col-lg-' + (12 - this.labellg);
44076                 }
44077                 if(this.labelmd > 0){
44078                     label.cls += ' col-md-' + this.labelmd;
44079                     container.cls += ' col-md-' + (12 - this.labelmd);
44080                 }
44081                 if(this.labelsm > 0){
44082                     label.cls += ' col-sm-' + this.labelsm;
44083                     container.cls += ' col-sm-' + (12 - this.labelsm);
44084                 }
44085                 if(this.labelxs > 0){
44086                     label.cls += ' col-xs-' + this.labelxs;
44087                     container.cls += ' col-xs-' + (12 - this.labelxs);
44088                 }
44089             }
44090         }
44091
44092         cfg.cn = [
44093             label,
44094             container,
44095             hiddenInput
44096         ];
44097         
44098         var settings = this;
44099
44100         ['xs','sm','md','lg'].map(function(size){
44101             if (settings[size]) {
44102                 cfg.cls += ' col-' + size + '-' + settings[size];
44103             }
44104         });
44105         
44106         return cfg;
44107     },
44108     
44109     initEvents : function()
44110     {
44111         this.indicator = this.indicatorEl();
44112         
44113         this.initCurrencyEvent();
44114         
44115         this.initNumberEvent();
44116     },
44117     
44118     initCurrencyEvent : function()
44119     {
44120         if (!this.store) {
44121             throw "can not find store for combo";
44122         }
44123         
44124         this.store = Roo.factory(this.store, Roo.data);
44125         this.store.parent = this;
44126         
44127         this.createList();
44128         
44129         this.triggerEl = this.el.select('.input-group-addon', true).first();
44130         
44131         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44132         
44133         var _this = this;
44134         
44135         (function(){
44136             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44137             _this.list.setWidth(lw);
44138         }).defer(100);
44139         
44140         this.list.on('mouseover', this.onViewOver, this);
44141         this.list.on('mousemove', this.onViewMove, this);
44142         this.list.on('scroll', this.onViewScroll, this);
44143         
44144         if(!this.tpl){
44145             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44146         }
44147         
44148         this.view = new Roo.View(this.list, this.tpl, {
44149             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44150         });
44151         
44152         this.view.on('click', this.onViewClick, this);
44153         
44154         this.store.on('beforeload', this.onBeforeLoad, this);
44155         this.store.on('load', this.onLoad, this);
44156         this.store.on('loadexception', this.onLoadException, this);
44157         
44158         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44159             "up" : function(e){
44160                 this.inKeyMode = true;
44161                 this.selectPrev();
44162             },
44163
44164             "down" : function(e){
44165                 if(!this.isExpanded()){
44166                     this.onTriggerClick();
44167                 }else{
44168                     this.inKeyMode = true;
44169                     this.selectNext();
44170                 }
44171             },
44172
44173             "enter" : function(e){
44174                 this.collapse();
44175                 
44176                 if(this.fireEvent("specialkey", this, e)){
44177                     this.onViewClick(false);
44178                 }
44179                 
44180                 return true;
44181             },
44182
44183             "esc" : function(e){
44184                 this.collapse();
44185             },
44186
44187             "tab" : function(e){
44188                 this.collapse();
44189                 
44190                 if(this.fireEvent("specialkey", this, e)){
44191                     this.onViewClick(false);
44192                 }
44193                 
44194                 return true;
44195             },
44196
44197             scope : this,
44198
44199             doRelay : function(foo, bar, hname){
44200                 if(hname == 'down' || this.scope.isExpanded()){
44201                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44202                 }
44203                 return true;
44204             },
44205
44206             forceKeyDown: true
44207         });
44208         
44209         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44210         
44211     },
44212     
44213     initNumberEvent : function(e)
44214     {
44215         this.inputEl().on("keydown" , this.fireKey,  this);
44216         this.inputEl().on("focus", this.onFocus,  this);
44217         this.inputEl().on("blur", this.onBlur,  this);
44218         
44219         this.inputEl().relayEvent('keyup', this);
44220         
44221         if(this.indicator){
44222             this.indicator.addClass('invisible');
44223         }
44224  
44225         this.originalValue = this.getValue();
44226         
44227         if(this.validationEvent == 'keyup'){
44228             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44229             this.inputEl().on('keyup', this.filterValidation, this);
44230         }
44231         else if(this.validationEvent !== false){
44232             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44233         }
44234         
44235         if(this.selectOnFocus){
44236             this.on("focus", this.preFocus, this);
44237             
44238         }
44239         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44240             this.inputEl().on("keypress", this.filterKeys, this);
44241         } else {
44242             this.inputEl().relayEvent('keypress', this);
44243         }
44244         
44245         var allowed = "0123456789";
44246         
44247         if(this.allowDecimals){
44248             allowed += this.decimalSeparator;
44249         }
44250         
44251         if(this.allowNegative){
44252             allowed += "-";
44253         }
44254         
44255         if(this.thousandsDelimiter) {
44256             allowed += ",";
44257         }
44258         
44259         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44260         
44261         var keyPress = function(e){
44262             
44263             var k = e.getKey();
44264             
44265             var c = e.getCharCode();
44266             
44267             if(
44268                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44269                     allowed.indexOf(String.fromCharCode(c)) === -1
44270             ){
44271                 e.stopEvent();
44272                 return;
44273             }
44274             
44275             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44276                 return;
44277             }
44278             
44279             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44280                 e.stopEvent();
44281             }
44282         };
44283         
44284         this.inputEl().on("keypress", keyPress, this);
44285         
44286     },
44287     
44288     onTriggerClick : function(e)
44289     {   
44290         if(this.disabled){
44291             return;
44292         }
44293         
44294         this.page = 0;
44295         this.loadNext = false;
44296         
44297         if(this.isExpanded()){
44298             this.collapse();
44299             return;
44300         }
44301         
44302         this.hasFocus = true;
44303         
44304         if(this.triggerAction == 'all') {
44305             this.doQuery(this.allQuery, true);
44306             return;
44307         }
44308         
44309         this.doQuery(this.getRawValue());
44310     },
44311     
44312     getCurrency : function()
44313     {   
44314         var v = this.currencyEl().getValue();
44315         
44316         return v;
44317     },
44318     
44319     restrictHeight : function()
44320     {
44321         this.list.alignTo(this.currencyEl(), this.listAlign);
44322         this.list.alignTo(this.currencyEl(), this.listAlign);
44323     },
44324     
44325     onViewClick : function(view, doFocus, el, e)
44326     {
44327         var index = this.view.getSelectedIndexes()[0];
44328         
44329         var r = this.store.getAt(index);
44330         
44331         if(r){
44332             this.onSelect(r, index);
44333         }
44334     },
44335     
44336     onSelect : function(record, index){
44337         
44338         if(this.fireEvent('beforeselect', this, record, index) !== false){
44339         
44340             this.setFromCurrencyData(index > -1 ? record.data : false);
44341             
44342             this.collapse();
44343             
44344             this.fireEvent('select', this, record, index);
44345         }
44346     },
44347     
44348     setFromCurrencyData : function(o)
44349     {
44350         var currency = '';
44351         
44352         this.lastCurrency = o;
44353         
44354         if (this.currencyField) {
44355             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44356         } else {
44357             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44358         }
44359         
44360         this.lastSelectionText = currency;
44361         
44362         //setting default currency
44363         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44364             this.setCurrency(this.defaultCurrency);
44365             return;
44366         }
44367         
44368         this.setCurrency(currency);
44369     },
44370     
44371     setFromData : function(o)
44372     {
44373         var c = {};
44374         
44375         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44376         
44377         this.setFromCurrencyData(c);
44378         
44379         var value = '';
44380         
44381         if (this.name) {
44382             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44383         } else {
44384             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44385         }
44386         
44387         this.setValue(value);
44388         
44389     },
44390     
44391     setCurrency : function(v)
44392     {   
44393         this.currencyValue = v;
44394         
44395         if(this.rendered){
44396             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44397             this.validate();
44398         }
44399     },
44400     
44401     setValue : function(v)
44402     {
44403         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44404         
44405         this.value = v;
44406         
44407         if(this.rendered){
44408             
44409             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44410             
44411             this.inputEl().dom.value = (v == '') ? '' :
44412                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44413             
44414             if(!this.allowZero && v === '0') {
44415                 this.hiddenEl().dom.value = '';
44416                 this.inputEl().dom.value = '';
44417             }
44418             
44419             this.validate();
44420         }
44421     },
44422     
44423     getRawValue : function()
44424     {
44425         var v = this.inputEl().getValue();
44426         
44427         return v;
44428     },
44429     
44430     getValue : function()
44431     {
44432         return this.fixPrecision(this.parseValue(this.getRawValue()));
44433     },
44434     
44435     parseValue : function(value)
44436     {
44437         if(this.thousandsDelimiter) {
44438             value += "";
44439             r = new RegExp(",", "g");
44440             value = value.replace(r, "");
44441         }
44442         
44443         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44444         return isNaN(value) ? '' : value;
44445         
44446     },
44447     
44448     fixPrecision : function(value)
44449     {
44450         if(this.thousandsDelimiter) {
44451             value += "";
44452             r = new RegExp(",", "g");
44453             value = value.replace(r, "");
44454         }
44455         
44456         var nan = isNaN(value);
44457         
44458         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44459             return nan ? '' : value;
44460         }
44461         return parseFloat(value).toFixed(this.decimalPrecision);
44462     },
44463     
44464     decimalPrecisionFcn : function(v)
44465     {
44466         return Math.floor(v);
44467     },
44468     
44469     validateValue : function(value)
44470     {
44471         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44472             return false;
44473         }
44474         
44475         var num = this.parseValue(value);
44476         
44477         if(isNaN(num)){
44478             this.markInvalid(String.format(this.nanText, value));
44479             return false;
44480         }
44481         
44482         if(num < this.minValue){
44483             this.markInvalid(String.format(this.minText, this.minValue));
44484             return false;
44485         }
44486         
44487         if(num > this.maxValue){
44488             this.markInvalid(String.format(this.maxText, this.maxValue));
44489             return false;
44490         }
44491         
44492         return true;
44493     },
44494     
44495     validate : function()
44496     {
44497         if(this.disabled || this.allowBlank){
44498             this.markValid();
44499             return true;
44500         }
44501         
44502         var currency = this.getCurrency();
44503         
44504         if(this.validateValue(this.getRawValue()) && currency.length){
44505             this.markValid();
44506             return true;
44507         }
44508         
44509         this.markInvalid();
44510         return false;
44511     },
44512     
44513     getName: function()
44514     {
44515         return this.name;
44516     },
44517     
44518     beforeBlur : function()
44519     {
44520         if(!this.castInt){
44521             return;
44522         }
44523         
44524         var v = this.parseValue(this.getRawValue());
44525         
44526         if(v || v == 0){
44527             this.setValue(v);
44528         }
44529     },
44530     
44531     onBlur : function()
44532     {
44533         this.beforeBlur();
44534         
44535         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44536             //this.el.removeClass(this.focusClass);
44537         }
44538         
44539         this.hasFocus = false;
44540         
44541         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44542             this.validate();
44543         }
44544         
44545         var v = this.getValue();
44546         
44547         if(String(v) !== String(this.startValue)){
44548             this.fireEvent('change', this, v, this.startValue);
44549         }
44550         
44551         this.fireEvent("blur", this);
44552     },
44553     
44554     inputEl : function()
44555     {
44556         return this.el.select('.roo-money-amount-input', true).first();
44557     },
44558     
44559     currencyEl : function()
44560     {
44561         return this.el.select('.roo-money-currency-input', true).first();
44562     },
44563     
44564     hiddenEl : function()
44565     {
44566         return this.el.select('input.hidden-number-input',true).first();
44567     }
44568     
44569 });/**
44570  * @class Roo.bootstrap.BezierSignature
44571  * @extends Roo.bootstrap.Component
44572  * Bootstrap BezierSignature class
44573  * This script refer to:
44574  *    Title: Signature Pad
44575  *    Author: szimek
44576  *    Availability: https://github.com/szimek/signature_pad
44577  *
44578  * @constructor
44579  * Create a new BezierSignature
44580  * @param {Object} config The config object
44581  */
44582
44583 Roo.bootstrap.BezierSignature = function(config){
44584     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44585     this.addEvents({
44586         "resize" : true
44587     });
44588 };
44589
44590 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44591 {
44592      
44593     curve_data: [],
44594     
44595     is_empty: true,
44596     
44597     mouse_btn_down: true,
44598     
44599     /**
44600      * @cfg {int} canvas height
44601      */
44602     canvas_height: '200px',
44603     
44604     /**
44605      * @cfg {float|function} Radius of a single dot.
44606      */ 
44607     dot_size: false,
44608     
44609     /**
44610      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44611      */
44612     min_width: 0.5,
44613     
44614     /**
44615      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44616      */
44617     max_width: 2.5,
44618     
44619     /**
44620      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44621      */
44622     throttle: 16,
44623     
44624     /**
44625      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44626      */
44627     min_distance: 5,
44628     
44629     /**
44630      * @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.
44631      */
44632     bg_color: 'rgba(0, 0, 0, 0)',
44633     
44634     /**
44635      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44636      */
44637     dot_color: 'black',
44638     
44639     /**
44640      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44641      */ 
44642     velocity_filter_weight: 0.7,
44643     
44644     /**
44645      * @cfg {function} Callback when stroke begin. 
44646      */
44647     onBegin: false,
44648     
44649     /**
44650      * @cfg {function} Callback when stroke end.
44651      */
44652     onEnd: false,
44653     
44654     getAutoCreate : function()
44655     {
44656         var cls = 'roo-signature column';
44657         
44658         if(this.cls){
44659             cls += ' ' + this.cls;
44660         }
44661         
44662         var col_sizes = [
44663             'lg',
44664             'md',
44665             'sm',
44666             'xs'
44667         ];
44668         
44669         for(var i = 0; i < col_sizes.length; i++) {
44670             if(this[col_sizes[i]]) {
44671                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44672             }
44673         }
44674         
44675         var cfg = {
44676             tag: 'div',
44677             cls: cls,
44678             cn: [
44679                 {
44680                     tag: 'div',
44681                     cls: 'roo-signature-body',
44682                     cn: [
44683                         {
44684                             tag: 'canvas',
44685                             cls: 'roo-signature-body-canvas',
44686                             height: this.canvas_height,
44687                             width: this.canvas_width
44688                         }
44689                     ]
44690                 },
44691                 {
44692                     tag: 'input',
44693                     type: 'file',
44694                     style: 'display: none'
44695                 }
44696             ]
44697         };
44698         
44699         return cfg;
44700     },
44701     
44702     initEvents: function() 
44703     {
44704         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44705         
44706         var canvas = this.canvasEl();
44707         
44708         // mouse && touch event swapping...
44709         canvas.dom.style.touchAction = 'none';
44710         canvas.dom.style.msTouchAction = 'none';
44711         
44712         this.mouse_btn_down = false;
44713         canvas.on('mousedown', this._handleMouseDown, this);
44714         canvas.on('mousemove', this._handleMouseMove, this);
44715         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44716         
44717         if (window.PointerEvent) {
44718             canvas.on('pointerdown', this._handleMouseDown, this);
44719             canvas.on('pointermove', this._handleMouseMove, this);
44720             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44721         }
44722         
44723         if ('ontouchstart' in window) {
44724             canvas.on('touchstart', this._handleTouchStart, this);
44725             canvas.on('touchmove', this._handleTouchMove, this);
44726             canvas.on('touchend', this._handleTouchEnd, this);
44727         }
44728         
44729         Roo.EventManager.onWindowResize(this.resize, this, true);
44730         
44731         // file input event
44732         this.fileEl().on('change', this.uploadImage, this);
44733         
44734         this.clear();
44735         
44736         this.resize();
44737     },
44738     
44739     resize: function(){
44740         
44741         var canvas = this.canvasEl().dom;
44742         var ctx = this.canvasElCtx();
44743         var img_data = false;
44744         
44745         if(canvas.width > 0) {
44746             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44747         }
44748         // setting canvas width will clean img data
44749         canvas.width = 0;
44750         
44751         var style = window.getComputedStyle ? 
44752             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44753             
44754         var padding_left = parseInt(style.paddingLeft) || 0;
44755         var padding_right = parseInt(style.paddingRight) || 0;
44756         
44757         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44758         
44759         if(img_data) {
44760             ctx.putImageData(img_data, 0, 0);
44761         }
44762     },
44763     
44764     _handleMouseDown: function(e)
44765     {
44766         if (e.browserEvent.which === 1) {
44767             this.mouse_btn_down = true;
44768             this.strokeBegin(e);
44769         }
44770     },
44771     
44772     _handleMouseMove: function (e)
44773     {
44774         if (this.mouse_btn_down) {
44775             this.strokeMoveUpdate(e);
44776         }
44777     },
44778     
44779     _handleMouseUp: function (e)
44780     {
44781         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44782             this.mouse_btn_down = false;
44783             this.strokeEnd(e);
44784         }
44785     },
44786     
44787     _handleTouchStart: function (e) {
44788         
44789         e.preventDefault();
44790         if (e.browserEvent.targetTouches.length === 1) {
44791             // var touch = e.browserEvent.changedTouches[0];
44792             // this.strokeBegin(touch);
44793             
44794              this.strokeBegin(e); // assume e catching the correct xy...
44795         }
44796     },
44797     
44798     _handleTouchMove: function (e) {
44799         e.preventDefault();
44800         // var touch = event.targetTouches[0];
44801         // _this._strokeMoveUpdate(touch);
44802         this.strokeMoveUpdate(e);
44803     },
44804     
44805     _handleTouchEnd: function (e) {
44806         var wasCanvasTouched = e.target === this.canvasEl().dom;
44807         if (wasCanvasTouched) {
44808             e.preventDefault();
44809             // var touch = event.changedTouches[0];
44810             // _this._strokeEnd(touch);
44811             this.strokeEnd(e);
44812         }
44813     },
44814     
44815     reset: function () {
44816         this._lastPoints = [];
44817         this._lastVelocity = 0;
44818         this._lastWidth = (this.min_width + this.max_width) / 2;
44819         this.canvasElCtx().fillStyle = this.dot_color;
44820     },
44821     
44822     strokeMoveUpdate: function(e)
44823     {
44824         this.strokeUpdate(e);
44825         
44826         if (this.throttle) {
44827             this.throttleStroke(this.strokeUpdate, this.throttle);
44828         }
44829         else {
44830             this.strokeUpdate(e);
44831         }
44832     },
44833     
44834     strokeBegin: function(e)
44835     {
44836         var newPointGroup = {
44837             color: this.dot_color,
44838             points: []
44839         };
44840         
44841         if (typeof this.onBegin === 'function') {
44842             this.onBegin(e);
44843         }
44844         
44845         this.curve_data.push(newPointGroup);
44846         this.reset();
44847         this.strokeUpdate(e);
44848     },
44849     
44850     strokeUpdate: function(e)
44851     {
44852         var rect = this.canvasEl().dom.getBoundingClientRect();
44853         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44854         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44855         var lastPoints = lastPointGroup.points;
44856         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44857         var isLastPointTooClose = lastPoint
44858             ? point.distanceTo(lastPoint) <= this.min_distance
44859             : false;
44860         var color = lastPointGroup.color;
44861         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44862             var curve = this.addPoint(point);
44863             if (!lastPoint) {
44864                 this.drawDot({color: color, point: point});
44865             }
44866             else if (curve) {
44867                 this.drawCurve({color: color, curve: curve});
44868             }
44869             lastPoints.push({
44870                 time: point.time,
44871                 x: point.x,
44872                 y: point.y
44873             });
44874         }
44875     },
44876     
44877     strokeEnd: function(e)
44878     {
44879         this.strokeUpdate(e);
44880         if (typeof this.onEnd === 'function') {
44881             this.onEnd(e);
44882         }
44883     },
44884     
44885     addPoint:  function (point) {
44886         var _lastPoints = this._lastPoints;
44887         _lastPoints.push(point);
44888         if (_lastPoints.length > 2) {
44889             if (_lastPoints.length === 3) {
44890                 _lastPoints.unshift(_lastPoints[0]);
44891             }
44892             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44893             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44894             _lastPoints.shift();
44895             return curve;
44896         }
44897         return null;
44898     },
44899     
44900     calculateCurveWidths: function (startPoint, endPoint) {
44901         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44902             (1 - this.velocity_filter_weight) * this._lastVelocity;
44903
44904         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44905         var widths = {
44906             end: newWidth,
44907             start: this._lastWidth
44908         };
44909         
44910         this._lastVelocity = velocity;
44911         this._lastWidth = newWidth;
44912         return widths;
44913     },
44914     
44915     drawDot: function (_a) {
44916         var color = _a.color, point = _a.point;
44917         var ctx = this.canvasElCtx();
44918         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44919         ctx.beginPath();
44920         this.drawCurveSegment(point.x, point.y, width);
44921         ctx.closePath();
44922         ctx.fillStyle = color;
44923         ctx.fill();
44924     },
44925     
44926     drawCurve: function (_a) {
44927         var color = _a.color, curve = _a.curve;
44928         var ctx = this.canvasElCtx();
44929         var widthDelta = curve.endWidth - curve.startWidth;
44930         var drawSteps = Math.floor(curve.length()) * 2;
44931         ctx.beginPath();
44932         ctx.fillStyle = color;
44933         for (var i = 0; i < drawSteps; i += 1) {
44934         var t = i / drawSteps;
44935         var tt = t * t;
44936         var ttt = tt * t;
44937         var u = 1 - t;
44938         var uu = u * u;
44939         var uuu = uu * u;
44940         var x = uuu * curve.startPoint.x;
44941         x += 3 * uu * t * curve.control1.x;
44942         x += 3 * u * tt * curve.control2.x;
44943         x += ttt * curve.endPoint.x;
44944         var y = uuu * curve.startPoint.y;
44945         y += 3 * uu * t * curve.control1.y;
44946         y += 3 * u * tt * curve.control2.y;
44947         y += ttt * curve.endPoint.y;
44948         var width = curve.startWidth + ttt * widthDelta;
44949         this.drawCurveSegment(x, y, width);
44950         }
44951         ctx.closePath();
44952         ctx.fill();
44953     },
44954     
44955     drawCurveSegment: function (x, y, width) {
44956         var ctx = this.canvasElCtx();
44957         ctx.moveTo(x, y);
44958         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44959         this.is_empty = false;
44960     },
44961     
44962     clear: function()
44963     {
44964         var ctx = this.canvasElCtx();
44965         var canvas = this.canvasEl().dom;
44966         ctx.fillStyle = this.bg_color;
44967         ctx.clearRect(0, 0, canvas.width, canvas.height);
44968         ctx.fillRect(0, 0, canvas.width, canvas.height);
44969         this.curve_data = [];
44970         this.reset();
44971         this.is_empty = true;
44972     },
44973     
44974     fileEl: function()
44975     {
44976         return  this.el.select('input',true).first();
44977     },
44978     
44979     canvasEl: function()
44980     {
44981         return this.el.select('canvas',true).first();
44982     },
44983     
44984     canvasElCtx: function()
44985     {
44986         return this.el.select('canvas',true).first().dom.getContext('2d');
44987     },
44988     
44989     getImage: function(type)
44990     {
44991         if(this.is_empty) {
44992             return false;
44993         }
44994         
44995         // encryption ?
44996         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44997     },
44998     
44999     drawFromImage: function(img_src)
45000     {
45001         var img = new Image();
45002         
45003         img.onload = function(){
45004             this.canvasElCtx().drawImage(img, 0, 0);
45005         }.bind(this);
45006         
45007         img.src = img_src;
45008         
45009         this.is_empty = false;
45010     },
45011     
45012     selectImage: function()
45013     {
45014         this.fileEl().dom.click();
45015     },
45016     
45017     uploadImage: function(e)
45018     {
45019         var reader = new FileReader();
45020         
45021         reader.onload = function(e){
45022             var img = new Image();
45023             img.onload = function(){
45024                 this.reset();
45025                 this.canvasElCtx().drawImage(img, 0, 0);
45026             }.bind(this);
45027             img.src = e.target.result;
45028         }.bind(this);
45029         
45030         reader.readAsDataURL(e.target.files[0]);
45031     },
45032     
45033     // Bezier Point Constructor
45034     Point: (function () {
45035         function Point(x, y, time) {
45036             this.x = x;
45037             this.y = y;
45038             this.time = time || Date.now();
45039         }
45040         Point.prototype.distanceTo = function (start) {
45041             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45042         };
45043         Point.prototype.equals = function (other) {
45044             return this.x === other.x && this.y === other.y && this.time === other.time;
45045         };
45046         Point.prototype.velocityFrom = function (start) {
45047             return this.time !== start.time
45048             ? this.distanceTo(start) / (this.time - start.time)
45049             : 0;
45050         };
45051         return Point;
45052     }()),
45053     
45054     
45055     // Bezier Constructor
45056     Bezier: (function () {
45057         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45058             this.startPoint = startPoint;
45059             this.control2 = control2;
45060             this.control1 = control1;
45061             this.endPoint = endPoint;
45062             this.startWidth = startWidth;
45063             this.endWidth = endWidth;
45064         }
45065         Bezier.fromPoints = function (points, widths, scope) {
45066             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45067             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45068             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45069         };
45070         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45071             var dx1 = s1.x - s2.x;
45072             var dy1 = s1.y - s2.y;
45073             var dx2 = s2.x - s3.x;
45074             var dy2 = s2.y - s3.y;
45075             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45076             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45077             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45078             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45079             var dxm = m1.x - m2.x;
45080             var dym = m1.y - m2.y;
45081             var k = l2 / (l1 + l2);
45082             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45083             var tx = s2.x - cm.x;
45084             var ty = s2.y - cm.y;
45085             return {
45086                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45087                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45088             };
45089         };
45090         Bezier.prototype.length = function () {
45091             var steps = 10;
45092             var length = 0;
45093             var px;
45094             var py;
45095             for (var i = 0; i <= steps; i += 1) {
45096                 var t = i / steps;
45097                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45098                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45099                 if (i > 0) {
45100                     var xdiff = cx - px;
45101                     var ydiff = cy - py;
45102                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45103                 }
45104                 px = cx;
45105                 py = cy;
45106             }
45107             return length;
45108         };
45109         Bezier.prototype.point = function (t, start, c1, c2, end) {
45110             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45111             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45112             + (3.0 * c2 * (1.0 - t) * t * t)
45113             + (end * t * t * t);
45114         };
45115         return Bezier;
45116     }()),
45117     
45118     throttleStroke: function(fn, wait) {
45119       if (wait === void 0) { wait = 250; }
45120       var previous = 0;
45121       var timeout = null;
45122       var result;
45123       var storedContext;
45124       var storedArgs;
45125       var later = function () {
45126           previous = Date.now();
45127           timeout = null;
45128           result = fn.apply(storedContext, storedArgs);
45129           if (!timeout) {
45130               storedContext = null;
45131               storedArgs = [];
45132           }
45133       };
45134       return function wrapper() {
45135           var args = [];
45136           for (var _i = 0; _i < arguments.length; _i++) {
45137               args[_i] = arguments[_i];
45138           }
45139           var now = Date.now();
45140           var remaining = wait - (now - previous);
45141           storedContext = this;
45142           storedArgs = args;
45143           if (remaining <= 0 || remaining > wait) {
45144               if (timeout) {
45145                   clearTimeout(timeout);
45146                   timeout = null;
45147               }
45148               previous = now;
45149               result = fn.apply(storedContext, storedArgs);
45150               if (!timeout) {
45151                   storedContext = null;
45152                   storedArgs = [];
45153               }
45154           }
45155           else if (!timeout) {
45156               timeout = window.setTimeout(later, remaining);
45157           }
45158           return result;
45159       };
45160   }
45161   
45162 });
45163
45164  
45165
45166