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 = 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     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * 
987  * @constructor
988  * Create a new button
989  * @param {Object} config The config object
990  */
991
992
993 Roo.bootstrap.Button = function(config){
994     Roo.bootstrap.Button.superclass.constructor.call(this, config);
995     
996     this.addEvents({
997         // raw events
998         /**
999          * @event click
1000          * When a butotn is pressed
1001          * @param {Roo.bootstrap.Button} btn
1002          * @param {Roo.EventObject} e
1003          */
1004         "click" : true,
1005          /**
1006          * @event toggle
1007          * After the button has been toggles
1008          * @param {Roo.bootstrap.Button} btn
1009          * @param {Roo.EventObject} e
1010          * @param {boolean} pressed (also available as button.pressed)
1011          */
1012         "toggle" : true
1013     });
1014 };
1015
1016 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1017     html: false,
1018     active: false,
1019     weight: '',
1020     badge_weight: '',
1021     outline : false,
1022     size: '',
1023     tag: 'button',
1024     href: '',
1025     disabled: false,
1026     isClose: false,
1027     glyphicon: '',
1028     fa: '',
1029     badge: '',
1030     theme: 'default',
1031     inverse: false,
1032     
1033     toggle: false,
1034     ontext: 'ON',
1035     offtext: 'OFF',
1036     defaulton: true,
1037     preventDefault: true,
1038     removeClass: false,
1039     name: false,
1040     target: false,
1041      
1042     pressed : null,
1043      
1044     
1045     getAutoCreate : function(){
1046         
1047         var cfg = {
1048             tag : 'button',
1049             cls : 'roo-button',
1050             html: ''
1051         };
1052         
1053         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1054             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1055             this.tag = 'button';
1056         } else {
1057             cfg.tag = this.tag;
1058         }
1059         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1060         
1061         if (this.toggle == true) {
1062             cfg={
1063                 tag: 'div',
1064                 cls: 'slider-frame roo-button',
1065                 cn: [
1066                     {
1067                         tag: 'span',
1068                         'data-on-text':'ON',
1069                         'data-off-text':'OFF',
1070                         cls: 'slider-button',
1071                         html: this.offtext
1072                     }
1073                 ]
1074             };
1075             // why are we validating the weights?
1076             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1077                 cfg.cls +=  ' ' + this.weight;
1078             }
1079             
1080             return cfg;
1081         }
1082         
1083         if (this.isClose) {
1084             cfg.cls += ' close';
1085             
1086             cfg["aria-hidden"] = true;
1087             
1088             cfg.html = "&times;";
1089             
1090             return cfg;
1091         }
1092              
1093         
1094         if (this.theme==='default') {
1095             cfg.cls = 'btn roo-button';
1096             
1097             //if (this.parentType != 'Navbar') {
1098             this.weight = this.weight.length ?  this.weight : 'default';
1099             //}
1100             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1101                 
1102                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1103                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1104                 cfg.cls += ' btn-' + outline + weight;
1105                 if (this.weight == 'default') {
1106                     // BC
1107                     cfg.cls += ' btn-' + this.weight;
1108                 }
1109             }
1110         } else if (this.theme==='glow') {
1111             
1112             cfg.tag = 'a';
1113             cfg.cls = 'btn-glow roo-button';
1114             
1115             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1116                 
1117                 cfg.cls += ' ' + this.weight;
1118             }
1119         }
1120    
1121         
1122         if (this.inverse) {
1123             this.cls += ' inverse';
1124         }
1125         
1126         
1127         if (this.active || this.pressed === true) {
1128             cfg.cls += ' active';
1129         }
1130         
1131         if (this.disabled) {
1132             cfg.disabled = 'disabled';
1133         }
1134         
1135         if (this.items) {
1136             Roo.log('changing to ul' );
1137             cfg.tag = 'ul';
1138             this.glyphicon = 'caret';
1139             if (Roo.bootstrap.version == 4) {
1140                 this.fa = 'caret-down';
1141             }
1142             
1143         }
1144         
1145         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1146          
1147         //gsRoo.log(this.parentType);
1148         if (this.parentType === 'Navbar' && !this.parent().bar) {
1149             Roo.log('changing to li?');
1150             
1151             cfg.tag = 'li';
1152             
1153             cfg.cls = '';
1154             cfg.cn =  [{
1155                 tag : 'a',
1156                 cls : 'roo-button',
1157                 html : this.html,
1158                 href : this.href || '#'
1159             }];
1160             if (this.menu) {
1161                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1162                 cfg.cls += ' dropdown';
1163             }   
1164             
1165             delete cfg.html;
1166             
1167         }
1168         
1169        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1170         
1171         if (this.glyphicon) {
1172             cfg.html = ' ' + cfg.html;
1173             
1174             cfg.cn = [
1175                 {
1176                     tag: 'span',
1177                     cls: 'glyphicon glyphicon-' + this.glyphicon
1178                 }
1179             ];
1180         }
1181         if (this.fa) {
1182             cfg.html = ' ' + cfg.html;
1183             
1184             cfg.cn = [
1185                 {
1186                     tag: 'i',
1187                     cls: 'fa fas fa-' + this.fa
1188                 }
1189             ];
1190         }
1191         
1192         if (this.badge) {
1193             cfg.html += ' ';
1194             
1195             cfg.tag = 'a';
1196             
1197 //            cfg.cls='btn roo-button';
1198             
1199             cfg.href=this.href;
1200             
1201             var value = cfg.html;
1202             
1203             if(this.glyphicon){
1204                 value = {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1207                     html: this.html
1208                 };
1209             }
1210             if(this.fa){
1211                 value = {
1212                     tag: 'i',
1213                     cls: 'fa fas fa-' + this.fa,
1214                     html: this.html
1215                 };
1216             }
1217             
1218             var bw = this.badge_weight.length ? this.badge_weight :
1219                 (this.weight.length ? this.weight : 'secondary');
1220             bw = bw == 'default' ? 'secondary' : bw;
1221             
1222             cfg.cn = [
1223                 value,
1224                 {
1225                     tag: 'span',
1226                     cls: 'badge badge-' + bw,
1227                     html: this.badge
1228                 }
1229             ];
1230             
1231             cfg.html='';
1232         }
1233         
1234         if (this.menu) {
1235             cfg.cls += ' dropdown';
1236             cfg.html = typeof(cfg.html) != 'undefined' ?
1237                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1238         }
1239         
1240         if (cfg.tag !== 'a' && this.href !== '') {
1241             throw "Tag must be a to set href.";
1242         } else if (this.href.length > 0) {
1243             cfg.href = this.href;
1244         }
1245         
1246         if(this.removeClass){
1247             cfg.cls = '';
1248         }
1249         
1250         if(this.target){
1251             cfg.target = this.target;
1252         }
1253         
1254         return cfg;
1255     },
1256     initEvents: function() {
1257        // Roo.log('init events?');
1258 //        Roo.log(this.el.dom);
1259         // add the menu...
1260         
1261         if (typeof (this.menu) != 'undefined') {
1262             this.menu.parentType = this.xtype;
1263             this.menu.triggerEl = this.el;
1264             this.addxtype(Roo.apply({}, this.menu));
1265         }
1266
1267
1268        if (this.el.hasClass('roo-button')) {
1269             this.el.on('click', this.onClick, this);
1270        } else {
1271             this.el.select('.roo-button').on('click', this.onClick, this);
1272        }
1273        
1274        if(this.removeClass){
1275            this.el.on('click', this.onClick, this);
1276        }
1277        
1278        this.el.enableDisplayMode();
1279         
1280     },
1281     onClick : function(e)
1282     {
1283         if (this.disabled) {
1284             return;
1285         }
1286         
1287         Roo.log('button on click ');
1288         if(this.preventDefault){
1289             e.preventDefault();
1290         }
1291         
1292         if (this.pressed === true || this.pressed === false) {
1293             this.toggleActive(e);
1294         }
1295         
1296         
1297         this.fireEvent('click', this, e);
1298     },
1299     
1300     /**
1301      * Enables this button
1302      */
1303     enable : function()
1304     {
1305         this.disabled = false;
1306         this.el.removeClass('disabled');
1307     },
1308     
1309     /**
1310      * Disable this button
1311      */
1312     disable : function()
1313     {
1314         this.disabled = true;
1315         this.el.addClass('disabled');
1316     },
1317      /**
1318      * sets the active state on/off, 
1319      * @param {Boolean} state (optional) Force a particular state
1320      */
1321     setActive : function(v) {
1322         
1323         this.el[v ? 'addClass' : 'removeClass']('active');
1324         this.pressed = v;
1325     },
1326      /**
1327      * toggles the current active state 
1328      */
1329     toggleActive : function(e)
1330     {
1331         this.setActive(!this.pressed); // this modifies pressed...
1332         this.fireEvent('toggle', this, e, this.pressed);
1333     },
1334      /**
1335      * get the current active state
1336      * @return {boolean} true if it's active
1337      */
1338     isActive : function()
1339     {
1340         return this.el.hasClass('active');
1341     },
1342     /**
1343      * set the text of the first selected button
1344      */
1345     setText : function(str)
1346     {
1347         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1348     },
1349     /**
1350      * get the text of the first selected button
1351      */
1352     getText : function()
1353     {
1354         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1355     },
1356     
1357     setWeight : function(str)
1358     {
1359         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1360         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1361         this.weight = str;
1362         var outline = this.outline ? 'outline-' : '';
1363         if (str == 'default') {
1364             this.el.addClass('btn-default btn-outline-secondary');        
1365             return;
1366         }
1367         this.el.addClass('btn-' + outline + str);        
1368     }
1369     
1370     
1371 });
1372 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1373
1374 Roo.bootstrap.Button.weights = [
1375     'default',
1376     'secondary' ,
1377     'primary',
1378     'success',
1379     'info',
1380     'warning',
1381     'danger',
1382     'link',
1383     'light',
1384     'dark'              
1385    
1386 ];/*
1387  * - LGPL
1388  *
1389  * column
1390  * 
1391  */
1392
1393 /**
1394  * @class Roo.bootstrap.Column
1395  * @extends Roo.bootstrap.Component
1396  * Bootstrap Column class
1397  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1398  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1399  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1400  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1401  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1402  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1403  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1404  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1405  *
1406  * 
1407  * @cfg {Boolean} hidden (true|false) hide the element
1408  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1409  * @cfg {String} fa (ban|check|...) font awesome icon
1410  * @cfg {Number} fasize (1|2|....) font awsome size
1411
1412  * @cfg {String} icon (info-sign|check|...) glyphicon name
1413
1414  * @cfg {String} html content of column.
1415  * 
1416  * @constructor
1417  * Create a new Column
1418  * @param {Object} config The config object
1419  */
1420
1421 Roo.bootstrap.Column = function(config){
1422     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1423 };
1424
1425 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1426     
1427     xs: false,
1428     sm: false,
1429     md: false,
1430     lg: false,
1431     xsoff: false,
1432     smoff: false,
1433     mdoff: false,
1434     lgoff: false,
1435     html: '',
1436     offset: 0,
1437     alert: false,
1438     fa: false,
1439     icon : false,
1440     hidden : false,
1441     fasize : 1,
1442     
1443     getAutoCreate : function(){
1444         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1445         
1446         cfg = {
1447             tag: 'div',
1448             cls: 'column'
1449         };
1450         
1451         var settings=this;
1452         var sizes =   ['xs','sm','md','lg'];
1453         sizes.map(function(size ,ix){
1454             //Roo.log( size + ':' + settings[size]);
1455             
1456             if (settings[size+'off'] !== false) {
1457                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1458             }
1459             
1460             if (settings[size] === false) {
1461                 return;
1462             }
1463             
1464             if (!settings[size]) { // 0 = hidden
1465                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1466                 // bootsrap4
1467                 for (var i = ix; i > -1; i--) {
1468                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1469                 }
1470                 
1471                 
1472                 return;
1473             }
1474             cfg.cls += ' col-' + size + '-' + settings[size] + (
1475                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1476             );
1477             
1478         });
1479         
1480         if (this.hidden) {
1481             cfg.cls += ' hidden';
1482         }
1483         
1484         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1485             cfg.cls +=' alert alert-' + this.alert;
1486         }
1487         
1488         
1489         if (this.html.length) {
1490             cfg.html = this.html;
1491         }
1492         if (this.fa) {
1493             var fasize = '';
1494             if (this.fasize > 1) {
1495                 fasize = ' fa-' + this.fasize + 'x';
1496             }
1497             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1498             
1499             
1500         }
1501         if (this.icon) {
1502             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1503         }
1504         
1505         return cfg;
1506     }
1507    
1508 });
1509
1510  
1511
1512  /*
1513  * - LGPL
1514  *
1515  * page container.
1516  * 
1517  */
1518
1519
1520 /**
1521  * @class Roo.bootstrap.Container
1522  * @extends Roo.bootstrap.Component
1523  * Bootstrap Container class
1524  * @cfg {Boolean} jumbotron is it a jumbotron element
1525  * @cfg {String} html content of element
1526  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1527  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1528  * @cfg {String} header content of header (for panel)
1529  * @cfg {String} footer content of footer (for panel)
1530  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1531  * @cfg {String} tag (header|aside|section) type of HTML tag.
1532  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1533  * @cfg {String} fa font awesome icon
1534  * @cfg {String} icon (info-sign|check|...) glyphicon name
1535  * @cfg {Boolean} hidden (true|false) hide the element
1536  * @cfg {Boolean} expandable (true|false) default false
1537  * @cfg {Boolean} expanded (true|false) default true
1538  * @cfg {String} rheader contet on the right of header
1539  * @cfg {Boolean} clickable (true|false) default false
1540
1541  *     
1542  * @constructor
1543  * Create a new Container
1544  * @param {Object} config The config object
1545  */
1546
1547 Roo.bootstrap.Container = function(config){
1548     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1549     
1550     this.addEvents({
1551         // raw events
1552          /**
1553          * @event expand
1554          * After the panel has been expand
1555          * 
1556          * @param {Roo.bootstrap.Container} this
1557          */
1558         "expand" : true,
1559         /**
1560          * @event collapse
1561          * After the panel has been collapsed
1562          * 
1563          * @param {Roo.bootstrap.Container} this
1564          */
1565         "collapse" : true,
1566         /**
1567          * @event click
1568          * When a element is chick
1569          * @param {Roo.bootstrap.Container} this
1570          * @param {Roo.EventObject} e
1571          */
1572         "click" : true
1573     });
1574 };
1575
1576 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1577     
1578     jumbotron : false,
1579     well: '',
1580     panel : '',
1581     header: '',
1582     footer : '',
1583     sticky: '',
1584     tag : false,
1585     alert : false,
1586     fa: false,
1587     icon : false,
1588     expandable : false,
1589     rheader : '',
1590     expanded : true,
1591     clickable: false,
1592   
1593      
1594     getChildContainer : function() {
1595         
1596         if(!this.el){
1597             return false;
1598         }
1599         
1600         if (this.panel.length) {
1601             return this.el.select('.panel-body',true).first();
1602         }
1603         
1604         return this.el;
1605     },
1606     
1607     
1608     getAutoCreate : function(){
1609         
1610         var cfg = {
1611             tag : this.tag || 'div',
1612             html : '',
1613             cls : ''
1614         };
1615         if (this.jumbotron) {
1616             cfg.cls = 'jumbotron';
1617         }
1618         
1619         
1620         
1621         // - this is applied by the parent..
1622         //if (this.cls) {
1623         //    cfg.cls = this.cls + '';
1624         //}
1625         
1626         if (this.sticky.length) {
1627             
1628             var bd = Roo.get(document.body);
1629             if (!bd.hasClass('bootstrap-sticky')) {
1630                 bd.addClass('bootstrap-sticky');
1631                 Roo.select('html',true).setStyle('height', '100%');
1632             }
1633              
1634             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1635         }
1636         
1637         
1638         if (this.well.length) {
1639             switch (this.well) {
1640                 case 'lg':
1641                 case 'sm':
1642                     cfg.cls +=' well well-' +this.well;
1643                     break;
1644                 default:
1645                     cfg.cls +=' well';
1646                     break;
1647             }
1648         }
1649         
1650         if (this.hidden) {
1651             cfg.cls += ' hidden';
1652         }
1653         
1654         
1655         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1656             cfg.cls +=' alert alert-' + this.alert;
1657         }
1658         
1659         var body = cfg;
1660         
1661         if (this.panel.length) {
1662             cfg.cls += ' panel panel-' + this.panel;
1663             cfg.cn = [];
1664             if (this.header.length) {
1665                 
1666                 var h = [];
1667                 
1668                 if(this.expandable){
1669                     
1670                     cfg.cls = cfg.cls + ' expandable';
1671                     
1672                     h.push({
1673                         tag: 'i',
1674                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1675                     });
1676                     
1677                 }
1678                 
1679                 h.push(
1680                     {
1681                         tag: 'span',
1682                         cls : 'panel-title',
1683                         html : (this.expandable ? '&nbsp;' : '') + this.header
1684                     },
1685                     {
1686                         tag: 'span',
1687                         cls: 'panel-header-right',
1688                         html: this.rheader
1689                     }
1690                 );
1691                 
1692                 cfg.cn.push({
1693                     cls : 'panel-heading',
1694                     style : this.expandable ? 'cursor: pointer' : '',
1695                     cn : h
1696                 });
1697                 
1698             }
1699             
1700             body = false;
1701             cfg.cn.push({
1702                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1703                 html : this.html
1704             });
1705             
1706             
1707             if (this.footer.length) {
1708                 cfg.cn.push({
1709                     cls : 'panel-footer',
1710                     html : this.footer
1711                     
1712                 });
1713             }
1714             
1715         }
1716         
1717         if (body) {
1718             body.html = this.html || cfg.html;
1719             // prefix with the icons..
1720             if (this.fa) {
1721                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1722             }
1723             if (this.icon) {
1724                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1725             }
1726             
1727             
1728         }
1729         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1730             cfg.cls =  'container';
1731         }
1732         
1733         return cfg;
1734     },
1735     
1736     initEvents: function() 
1737     {
1738         if(this.expandable){
1739             var headerEl = this.headerEl();
1740         
1741             if(headerEl){
1742                 headerEl.on('click', this.onToggleClick, this);
1743             }
1744         }
1745         
1746         if(this.clickable){
1747             this.el.on('click', this.onClick, this);
1748         }
1749         
1750     },
1751     
1752     onToggleClick : function()
1753     {
1754         var headerEl = this.headerEl();
1755         
1756         if(!headerEl){
1757             return;
1758         }
1759         
1760         if(this.expanded){
1761             this.collapse();
1762             return;
1763         }
1764         
1765         this.expand();
1766     },
1767     
1768     expand : function()
1769     {
1770         if(this.fireEvent('expand', this)) {
1771             
1772             this.expanded = true;
1773             
1774             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1775             
1776             this.el.select('.panel-body',true).first().removeClass('hide');
1777             
1778             var toggleEl = this.toggleEl();
1779
1780             if(!toggleEl){
1781                 return;
1782             }
1783
1784             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1785         }
1786         
1787     },
1788     
1789     collapse : function()
1790     {
1791         if(this.fireEvent('collapse', this)) {
1792             
1793             this.expanded = false;
1794             
1795             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1796             this.el.select('.panel-body',true).first().addClass('hide');
1797         
1798             var toggleEl = this.toggleEl();
1799
1800             if(!toggleEl){
1801                 return;
1802             }
1803
1804             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1805         }
1806     },
1807     
1808     toggleEl : function()
1809     {
1810         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1811             return;
1812         }
1813         
1814         return this.el.select('.panel-heading .fa',true).first();
1815     },
1816     
1817     headerEl : function()
1818     {
1819         if(!this.el || !this.panel.length || !this.header.length){
1820             return;
1821         }
1822         
1823         return this.el.select('.panel-heading',true).first()
1824     },
1825     
1826     bodyEl : function()
1827     {
1828         if(!this.el || !this.panel.length){
1829             return;
1830         }
1831         
1832         return this.el.select('.panel-body',true).first()
1833     },
1834     
1835     titleEl : function()
1836     {
1837         if(!this.el || !this.panel.length || !this.header.length){
1838             return;
1839         }
1840         
1841         return this.el.select('.panel-title',true).first();
1842     },
1843     
1844     setTitle : function(v)
1845     {
1846         var titleEl = this.titleEl();
1847         
1848         if(!titleEl){
1849             return;
1850         }
1851         
1852         titleEl.dom.innerHTML = v;
1853     },
1854     
1855     getTitle : function()
1856     {
1857         
1858         var titleEl = this.titleEl();
1859         
1860         if(!titleEl){
1861             return '';
1862         }
1863         
1864         return titleEl.dom.innerHTML;
1865     },
1866     
1867     setRightTitle : function(v)
1868     {
1869         var t = this.el.select('.panel-header-right',true).first();
1870         
1871         if(!t){
1872             return;
1873         }
1874         
1875         t.dom.innerHTML = v;
1876     },
1877     
1878     onClick : function(e)
1879     {
1880         e.preventDefault();
1881         
1882         this.fireEvent('click', this, e);
1883     }
1884 });
1885
1886  /*
1887  *  - LGPL
1888  *
1889  *  This is BS4's Card element.. - similar to our containers probably..
1890  * 
1891  */
1892 /**
1893  * @class Roo.bootstrap.Card
1894  * @extends Roo.bootstrap.Component
1895  * Bootstrap Card class
1896  *
1897  *
1898  * possible... may not be implemented..
1899  * @cfg {String} header_image  src url of image.
1900  * @cfg {String|Object} header
1901  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1902  * 
1903  * @cfg {String} title
1904  * @cfg {String} subtitle
1905  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1906  * @cfg {String} footer
1907  
1908  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1909  * 
1910  * @cfg {String} margin (0|1|2|3|4|5|auto)
1911  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1912  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1913  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1914  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1915  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1916  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1917  *
1918  * @cfg {String} padding (0|1|2|3|4|5)
1919  * @cfg {String} padding_top (0|1|2|3|4|5)
1920  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1921  * @cfg {String} padding_left (0|1|2|3|4|5)
1922  * @cfg {String} padding_right (0|1|2|3|4|5)
1923  * @cfg {String} padding_x (0|1|2|3|4|5)
1924  * @cfg {String} padding_y (0|1|2|3|4|5)
1925  *
1926  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1927  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1928  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1929  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1930  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1931  
1932  * @config {Boolean} dragable  if this card can be dragged.
1933  * @config {String} drag_group  group for drag
1934  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1935  * @config {String} drop_group  group for drag
1936  * 
1937  * @config {Boolean} collapsable can the body be collapsed.
1938  * @config {Boolean} collapsed is the body collapsed when rendered...
1939  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1940  * @config {Boolean} rotated is the body rotated when rendered...
1941  * 
1942  * @constructor
1943  * Create a new Container
1944  * @param {Object} config The config object
1945  */
1946
1947 Roo.bootstrap.Card = function(config){
1948     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1949     
1950     this.addEvents({
1951          // raw events
1952         /**
1953          * @event drop
1954          * When a element a card is dropped
1955          * @param {Roo.bootstrap.Card} this
1956          *
1957          * 
1958          * @param {Roo.bootstrap.Card} move_card the card being dropped?
1959          * @param {String} position 'above' or 'below'
1960          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
1961         
1962          */
1963         'drop' : true,
1964          /**
1965          * @event rotate
1966          * When a element a card is rotate
1967          * @param {Roo.bootstrap.Element} this
1968          * @param {Roo.Element} n the node being dropped?
1969          * @param {Boolean} rotate status
1970          */
1971         'rotate' : true
1972         
1973     });
1974 };
1975
1976
1977 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
1978     
1979     
1980     weight : '',
1981     
1982     margin: '', /// may be better in component?
1983     margin_top: '', 
1984     margin_bottom: '', 
1985     margin_left: '',
1986     margin_right: '',
1987     margin_x: '',
1988     margin_y: '',
1989     
1990     padding : '',
1991     padding_top: '', 
1992     padding_bottom: '', 
1993     padding_left: '',
1994     padding_right: '',
1995     padding_x: '',
1996     padding_y: '',
1997     
1998     display: '', 
1999     display_xs: '', 
2000     display_sm: '', 
2001     display_lg: '',
2002     display_xl: '',
2003  
2004     header_image  : '',
2005     header : '',
2006     header_size : 0,
2007     title : '',
2008     subtitle : '',
2009     html : '',
2010     footer: '',
2011
2012     collapsable : false,
2013     collapsed : false,
2014     rotateable : false,
2015     rotated : false,
2016     
2017     dragable : false,
2018     drag_group : false,
2019     dropable : false,
2020     drop_group : false,
2021     childContainer : false,
2022     dropEl : false, /// the dom placeholde element that indicates drop location.
2023     containerEl: false, // body container
2024     bodyEl: false, // card-body
2025     headerContainerEl : false, //
2026     headerEl : false,
2027     
2028     layoutCls : function()
2029     {
2030         var cls = '';
2031         var t = this;
2032         Roo.log(this.margin_bottom.length);
2033         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2034             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2035             
2036             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2037                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2038             }
2039             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2040                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2041             }
2042         });
2043         
2044         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2045             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2046                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2047             }
2048         });
2049         
2050         // more generic support?
2051         if (this.hidden) {
2052             cls += ' d-none';
2053         }
2054         
2055         return cls;
2056     },
2057  
2058        // Roo.log("Call onRender: " + this.xtype);
2059         /*  We are looking at something like this.
2060 <div class="card">
2061     <img src="..." class="card-img-top" alt="...">
2062     <div class="card-body">
2063         <h5 class="card-title">Card title</h5>
2064          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2065
2066         >> this bit is really the body...
2067         <div> << we will ad dthis in hopefully it will not break shit.
2068         
2069         ** card text does not actually have any styling...
2070         
2071             <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>
2072         
2073         </div> <<
2074           <a href="#" class="card-link">Card link</a>
2075           
2076     </div>
2077     <div class="card-footer">
2078         <small class="text-muted">Last updated 3 mins ago</small>
2079     </div>
2080 </div>
2081          */
2082     getAutoCreate : function(){
2083         
2084         var cfg = {
2085             tag : 'div',
2086             cls : 'card',
2087             cn : [ ]
2088         };
2089         
2090         if (this.weight.length && this.weight != 'light') {
2091             cfg.cls += ' text-white';
2092         } else {
2093             cfg.cls += ' text-dark'; // need as it's nested..
2094         }
2095         if (this.weight.length) {
2096             cfg.cls += ' bg-' + this.weight;
2097         }
2098         
2099         cfg.cls += this.layoutCls(); 
2100         
2101         var hdr = false;
2102         var hdr_ctr = false;
2103         if (this.header.length) {
2104             hdr = {
2105                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2106                 cls : 'card-header',
2107                 cn : []
2108             };
2109             cfg.cn.push(hdr);
2110             hdr_ctr = hdr;
2111         } else {
2112             hdr = {
2113                 tag : 'div',
2114                 cls : 'card-header d-none',
2115                 cn : []
2116             };
2117             cfg.cn.push(hdr);
2118             hdr_ctr = hdr;
2119         }
2120         if (this.collapsable) {
2121             hdr_ctr = {
2122             tag : 'a',
2123             cls : 'd-block user-select-none',
2124             cn: [
2125                     {
2126                         tag: 'i',
2127                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2128                     }
2129                    
2130                 ]
2131             };
2132             hdr.cn.push(hdr_ctr);
2133         }
2134         
2135         hdr_ctr.cn.push(        {
2136             tag: 'span',
2137             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2138             html : this.header
2139         });
2140         
2141         
2142         if (this.header_image.length) {
2143             cfg.cn.push({
2144                 tag : 'img',
2145                 cls : 'card-img-top',
2146                 src: this.header_image // escape?
2147             });
2148         } else {
2149             cfg.cn.push({
2150                     tag : 'div',
2151                     cls : 'card-img-top d-none' 
2152                 });
2153         }
2154             
2155         var body = {
2156             tag : 'div',
2157             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2158             cn : []
2159         };
2160         var obody = body;
2161         if (this.collapsable || this.rotateable) {
2162             obody = {
2163                 tag: 'div',
2164                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2165                 cn : [  body ]
2166             };
2167         }
2168         
2169         cfg.cn.push(obody);
2170         
2171         if (this.title.length) {
2172             body.cn.push({
2173                 tag : 'div',
2174                 cls : 'card-title',
2175                 src: this.title // escape?
2176             });
2177         }  
2178         
2179         if (this.subtitle.length) {
2180             body.cn.push({
2181                 tag : 'div',
2182                 cls : 'card-title',
2183                 src: this.subtitle // escape?
2184             });
2185         }
2186         
2187         body.cn.push({
2188             tag : 'div',
2189             cls : 'roo-card-body-ctr'
2190         });
2191         
2192         if (this.html.length) {
2193             body.cn.push({
2194                 tag: 'div',
2195                 html : this.html
2196             });
2197         }
2198         // fixme ? handle objects?
2199         
2200         if (this.footer.length) {
2201            
2202             cfg.cn.push({
2203                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2204                 html : this.footer
2205             });
2206             
2207         } else {
2208             cfg.cn.push({cls : 'card-footer d-none'});
2209         }
2210         
2211         // footer...
2212         
2213         return cfg;
2214     },
2215     
2216     
2217     getCardHeader : function()
2218     {
2219         var  ret = this.el.select('.card-header',true).first();
2220         if (ret.hasClass('d-none')) {
2221             ret.removeClass('d-none');
2222         }
2223         
2224         return ret;
2225     },
2226     getCardFooter : function()
2227     {
2228         var  ret = this.el.select('.card-footer',true).first();
2229         if (ret.hasClass('d-none')) {
2230             ret.removeClass('d-none');
2231         }
2232         
2233         return ret;
2234     },
2235     getCardImageTop : function()
2236     {
2237         var  ret = this.el.select('.card-img-top',true).first();
2238         if (ret.hasClass('d-none')) {
2239             ret.removeClass('d-none');
2240         }
2241             
2242         return ret;
2243     },
2244     
2245     getChildContainer : function()
2246     {
2247         
2248         if(!this.el){
2249             return false;
2250         }
2251         return this.el.select('.roo-card-body-ctr',true).first();    
2252     },
2253     
2254     initEvents: function() 
2255     {
2256         this.bodyEl = this.el.select('.card-body',true).first(); 
2257         this.containerEl = this.getChildContainer();
2258         if(this.dragable){
2259             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2260                     containerScroll: true,
2261                     ddGroup: this.drag_group || 'default_card_drag_group'
2262             });
2263             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2264         }
2265         if (this.dropable) {
2266             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2267                 containerScroll: true,
2268                 ddGroup: this.drop_group || 'default_card_drag_group'
2269             });
2270             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2271             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2272             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2273             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2274             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2275         }
2276         
2277         if (this.collapsable) {
2278             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2279         }
2280         if (this.rotateable) {
2281             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2282         }
2283         this.collapsableEl = this.el.select('.roo-collapsable').first();
2284          
2285         this.footerEl = this.el.select('.card-footer').first();
2286         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2287         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2288         this.headerEl = this.el.select('.card-header',true).first();
2289         
2290         if (this.rotated) {
2291             this.el.addClass('roo-card-rotated');
2292             this.fireEvent('rotate', this, true);
2293         }
2294         
2295     },
2296     getDragData : function(e)
2297     {
2298         var target = this.getEl();
2299         if (target) {
2300             //this.handleSelection(e);
2301             
2302             var dragData = {
2303                 source: this,
2304                 copy: false,
2305                 nodes: this.getEl(),
2306                 records: []
2307             };
2308             
2309             
2310             dragData.ddel = target.dom ;    // the div element
2311             Roo.log(target.getWidth( ));
2312             dragData.ddel.style.width = target.getWidth() + 'px';
2313             
2314             return dragData;
2315         }
2316         return false;
2317     },
2318     /**
2319     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2320     *    whole Element becomes the target, and this causes the drop gesture to append.
2321     */
2322     getTargetFromEvent : function(e, dragged_card_el)
2323     {
2324         var target = e.getTarget();
2325         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2326             target = target.parentNode;
2327         }
2328         
2329         var ret = {
2330             position: '',
2331             cards : [],
2332             card_n : -1,
2333             items_n : -1,
2334             card : false 
2335         };
2336         
2337         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2338         // see if target is one of the 'cards'...
2339         
2340         
2341         //Roo.log(this.items.length);
2342         var pos = false;
2343         
2344         var last_card_n = 0;
2345         var cards_len  = 0;
2346         for (var i = 0;i< this.items.length;i++) {
2347             
2348             if (!this.items[i].el.hasClass('card')) {
2349                  continue;
2350             }
2351             pos = this.getDropPoint(e, this.items[i].el.dom);
2352             
2353             cards_len = ret.cards.length;
2354             //Roo.log(this.items[i].el.dom.id);
2355             ret.cards.push(this.items[i]);
2356             last_card_n  = i;
2357             if (ret.card_n < 0 && pos == 'above') {
2358                 ret.position = cards_len > 0 ? 'below' : pos;
2359                 ret.items_n = i > 0 ? i - 1 : 0;
2360                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2361                 ret.card = ret.cards[ret.card_n];
2362             }
2363         }
2364         if (!ret.cards.length) {
2365             ret.card = true;
2366             ret.position = 'below';
2367             ret.items_n;
2368             return ret;
2369         }
2370         // could not find a card.. stick it at the end..
2371         if (ret.card_n < 0) {
2372             ret.card_n = last_card_n;
2373             ret.card = ret.cards[last_card_n];
2374             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2375             ret.position = 'below';
2376         }
2377         
2378         if (this.items[ret.items_n].el == dragged_card_el) {
2379             return false;
2380         }
2381         
2382         if (ret.position == 'below') {
2383             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2384             
2385             if (card_after  && card_after.el == dragged_card_el) {
2386                 return false;
2387             }
2388             return ret;
2389         }
2390         
2391         // its's after ..
2392         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2393         
2394         if (card_before  && card_before.el == dragged_card_el) {
2395             return false;
2396         }
2397         
2398         return ret;
2399     },
2400     
2401     onNodeEnter : function(n, dd, e, data){
2402         return false;
2403     },
2404     onNodeOver : function(n, dd, e, data)
2405     {
2406        
2407         var target_info = this.getTargetFromEvent(e,data.source.el);
2408         if (target_info === false) {
2409             this.dropPlaceHolder('hide');
2410             return false;
2411         }
2412         Roo.log(['getTargetFromEvent', target_info ]);
2413         
2414          
2415         this.dropPlaceHolder('show', target_info,data);
2416         
2417         return false; 
2418     },
2419     onNodeOut : function(n, dd, e, data){
2420         this.dropPlaceHolder('hide');
2421      
2422     },
2423     onNodeDrop : function(n, dd, e, data)
2424     {
2425         
2426         // call drop - return false if
2427         
2428         // this could actually fail - if the Network drops..
2429         // we will ignore this at present..- client should probably reload
2430         // the whole set of cards if stuff like that fails.
2431         
2432         
2433         var info = this.getTargetFromEvent(e,data.source.el);
2434         if (info === false) {
2435             return false;
2436         }
2437         this.dropPlaceHolder('hide');
2438   
2439          
2440     
2441     
2442     
2443         this.acceptCard(data.source, info.position, info.card, info.items_n);
2444         return true;
2445          
2446     },
2447     firstChildCard : function()
2448     {
2449         for (var i = 0;i< this.items.length;i++) {
2450             
2451             if (!this.items[i].el.hasClass('card')) {
2452                  continue;
2453             }
2454             return this.items[i];
2455         }
2456         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2457     },
2458     /**
2459      * accept card
2460      *
2461      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2462
2463      */
2464     acceptCard : function(move_card,  position, next_to_card )
2465     {
2466         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2467             return false;
2468         }
2469         
2470         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2471         
2472         
2473         var dom = move_card.el.dom;
2474         dom.parentNode.removeChild(dom);
2475         
2476         
2477         if (next_to_card !== false) {
2478             var cardel = next_to_card.el.dom;
2479             
2480             if (position == 'above') {
2481                 cardel.parentNode.insertBefore(dom, cardel);
2482             } else if (cardel.nextSibling) {
2483                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2484             } else {
2485                 cardel.parentNode.append(dom);
2486             }
2487         } else {
2488             // card container???
2489             this.containerEl.dom.append(dom);
2490         }
2491         
2492         //FIXME HANDLE card = true 
2493         
2494         // add this to the correct place in items.
2495         
2496         
2497         
2498         // remove Card from items.
2499         
2500         var old_parent = move_card.parent();
2501         
2502         old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2503         
2504         if (this.items.length) {
2505             var nitems = [];
2506             //Roo.log([info.items_n, info.position, this.items.length]);
2507             for (var i =0; i < this.items.length; i++) {
2508                 if (i == to_items_n && position == 'above') {
2509                     nitems.push(move_card);
2510                 }
2511                 nitems.push(this.items[i]);
2512                 if (i == to_items_n && position == 'below') {
2513                     nitems.push(move_card);
2514                 }
2515             }
2516             this.items = nitems;
2517             Roo.log(this.items);
2518         } else {
2519             this.items.push(move_card);
2520         }
2521         
2522         move_card.parentId = this.id;
2523         
2524         return true;
2525         
2526         
2527     },
2528     
2529     
2530     /**    Decide whether to drop above or below a View node. */
2531     getDropPoint : function(e, n, dd)
2532     {
2533         if (dd) {
2534              return false;
2535         }
2536         if (n == this.containerEl.dom) {
2537             return "above";
2538         }
2539         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2540         var c = t + (b - t) / 2;
2541         var y = Roo.lib.Event.getPageY(e);
2542         if(y <= c) {
2543             return "above";
2544         }else{
2545             return "below";
2546         }
2547     },
2548     onToggleCollapse : function(e)
2549         {
2550         if (this.collapsed) {
2551             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2552             this.collapsableEl.addClass('show');
2553             this.collapsed = false;
2554             return;
2555         }
2556         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2557         this.collapsableEl.removeClass('show');
2558         this.collapsed = true;
2559         
2560     
2561     },
2562     
2563     onToggleRotate : function(e)
2564     {
2565         this.collapsableEl.removeClass('show');
2566         this.footerEl.removeClass('d-none');
2567         this.el.removeClass('roo-card-rotated');
2568         this.el.removeClass('d-none');
2569         if (this.rotated) {
2570             
2571             this.collapsableEl.addClass('show');
2572             this.rotated = false;
2573             this.fireEvent('rotate', this, this.rotated);
2574             return;
2575         }
2576         this.el.addClass('roo-card-rotated');
2577         this.footerEl.addClass('d-none');
2578         this.el.select('.roo-collapsable').removeClass('show');
2579         
2580         this.rotated = true;
2581         this.fireEvent('rotate', this, this.rotated);
2582     
2583     },
2584     
2585     dropPlaceHolder: function (action, info, data)
2586     {
2587         if (this.dropEl === false) {
2588             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2589             cls : 'd-none'
2590             },true);
2591         }
2592         this.dropEl.removeClass(['d-none', 'd-block']);        
2593         if (action == 'hide') {
2594             
2595             this.dropEl.addClass('d-none');
2596             return;
2597         }
2598         // FIXME - info.card == true!!!
2599         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2600         
2601         if (info.card !== true) {
2602             var cardel = info.card.el.dom;
2603             
2604             if (info.position == 'above') {
2605                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2606             } else if (cardel.nextSibling) {
2607                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2608             } else {
2609                 cardel.parentNode.append(this.dropEl.dom);
2610             }
2611         } else {
2612             // card container???
2613             this.containerEl.dom.append(this.dropEl.dom);
2614         }
2615         
2616         this.dropEl.addClass('d-block roo-card-dropzone');
2617         
2618         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2619         
2620         
2621     
2622     
2623     
2624     },
2625     setHeaderText: function(html)
2626     {
2627         this.headerContainerEl.dom.innerHTML = html;
2628     }
2629
2630     
2631 });
2632
2633 /*
2634  * - LGPL
2635  *
2636  * Card header - holder for the card header elements.
2637  * 
2638  */
2639
2640 /**
2641  * @class Roo.bootstrap.CardHeader
2642  * @extends Roo.bootstrap.Element
2643  * Bootstrap CardHeader class
2644  * @constructor
2645  * Create a new Card Header - that you can embed children into
2646  * @param {Object} config The config object
2647  */
2648
2649 Roo.bootstrap.CardHeader = function(config){
2650     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2651 };
2652
2653 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2654     
2655     
2656     container_method : 'getCardHeader' 
2657     
2658      
2659     
2660     
2661    
2662 });
2663
2664  
2665
2666  /*
2667  * - LGPL
2668  *
2669  * Card footer - holder for the card footer elements.
2670  * 
2671  */
2672
2673 /**
2674  * @class Roo.bootstrap.CardFooter
2675  * @extends Roo.bootstrap.Element
2676  * Bootstrap CardFooter class
2677  * @constructor
2678  * Create a new Card Footer - that you can embed children into
2679  * @param {Object} config The config object
2680  */
2681
2682 Roo.bootstrap.CardFooter = function(config){
2683     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2684 };
2685
2686 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2687     
2688     
2689     container_method : 'getCardFooter' 
2690     
2691      
2692     
2693     
2694    
2695 });
2696
2697  
2698
2699  /*
2700  * - LGPL
2701  *
2702  * Card header - holder for the card header elements.
2703  * 
2704  */
2705
2706 /**
2707  * @class Roo.bootstrap.CardImageTop
2708  * @extends Roo.bootstrap.Element
2709  * Bootstrap CardImageTop class
2710  * @constructor
2711  * Create a new Card Image Top container
2712  * @param {Object} config The config object
2713  */
2714
2715 Roo.bootstrap.CardImageTop = function(config){
2716     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2717 };
2718
2719 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2720     
2721    
2722     container_method : 'getCardImageTop' 
2723     
2724      
2725     
2726    
2727 });
2728
2729  
2730
2731  /*
2732  * - LGPL
2733  *
2734  * image
2735  * 
2736  */
2737
2738
2739 /**
2740  * @class Roo.bootstrap.Img
2741  * @extends Roo.bootstrap.Component
2742  * Bootstrap Img class
2743  * @cfg {Boolean} imgResponsive false | true
2744  * @cfg {String} border rounded | circle | thumbnail
2745  * @cfg {String} src image source
2746  * @cfg {String} alt image alternative text
2747  * @cfg {String} href a tag href
2748  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2749  * @cfg {String} xsUrl xs image source
2750  * @cfg {String} smUrl sm image source
2751  * @cfg {String} mdUrl md image source
2752  * @cfg {String} lgUrl lg image source
2753  * 
2754  * @constructor
2755  * Create a new Input
2756  * @param {Object} config The config object
2757  */
2758
2759 Roo.bootstrap.Img = function(config){
2760     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2761     
2762     this.addEvents({
2763         // img events
2764         /**
2765          * @event click
2766          * The img click event for the img.
2767          * @param {Roo.EventObject} e
2768          */
2769         "click" : true
2770     });
2771 };
2772
2773 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2774     
2775     imgResponsive: true,
2776     border: '',
2777     src: 'about:blank',
2778     href: false,
2779     target: false,
2780     xsUrl: '',
2781     smUrl: '',
2782     mdUrl: '',
2783     lgUrl: '',
2784
2785     getAutoCreate : function()
2786     {   
2787         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2788             return this.createSingleImg();
2789         }
2790         
2791         var cfg = {
2792             tag: 'div',
2793             cls: 'roo-image-responsive-group',
2794             cn: []
2795         };
2796         var _this = this;
2797         
2798         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2799             
2800             if(!_this[size + 'Url']){
2801                 return;
2802             }
2803             
2804             var img = {
2805                 tag: 'img',
2806                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2807                 html: _this.html || cfg.html,
2808                 src: _this[size + 'Url']
2809             };
2810             
2811             img.cls += ' roo-image-responsive-' + size;
2812             
2813             var s = ['xs', 'sm', 'md', 'lg'];
2814             
2815             s.splice(s.indexOf(size), 1);
2816             
2817             Roo.each(s, function(ss){
2818                 img.cls += ' hidden-' + ss;
2819             });
2820             
2821             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2822                 cfg.cls += ' img-' + _this.border;
2823             }
2824             
2825             if(_this.alt){
2826                 cfg.alt = _this.alt;
2827             }
2828             
2829             if(_this.href){
2830                 var a = {
2831                     tag: 'a',
2832                     href: _this.href,
2833                     cn: [
2834                         img
2835                     ]
2836                 };
2837
2838                 if(this.target){
2839                     a.target = _this.target;
2840                 }
2841             }
2842             
2843             cfg.cn.push((_this.href) ? a : img);
2844             
2845         });
2846         
2847         return cfg;
2848     },
2849     
2850     createSingleImg : function()
2851     {
2852         var cfg = {
2853             tag: 'img',
2854             cls: (this.imgResponsive) ? 'img-responsive' : '',
2855             html : null,
2856             src : 'about:blank'  // just incase src get's set to undefined?!?
2857         };
2858         
2859         cfg.html = this.html || cfg.html;
2860         
2861         cfg.src = this.src || cfg.src;
2862         
2863         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2864             cfg.cls += ' img-' + this.border;
2865         }
2866         
2867         if(this.alt){
2868             cfg.alt = this.alt;
2869         }
2870         
2871         if(this.href){
2872             var a = {
2873                 tag: 'a',
2874                 href: this.href,
2875                 cn: [
2876                     cfg
2877                 ]
2878             };
2879             
2880             if(this.target){
2881                 a.target = this.target;
2882             }
2883             
2884         }
2885         
2886         return (this.href) ? a : cfg;
2887     },
2888     
2889     initEvents: function() 
2890     {
2891         if(!this.href){
2892             this.el.on('click', this.onClick, this);
2893         }
2894         
2895     },
2896     
2897     onClick : function(e)
2898     {
2899         Roo.log('img onclick');
2900         this.fireEvent('click', this, e);
2901     },
2902     /**
2903      * Sets the url of the image - used to update it
2904      * @param {String} url the url of the image
2905      */
2906     
2907     setSrc : function(url)
2908     {
2909         this.src =  url;
2910         
2911         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2912             this.el.dom.src =  url;
2913             return;
2914         }
2915         
2916         this.el.select('img', true).first().dom.src =  url;
2917     }
2918     
2919     
2920    
2921 });
2922
2923  /*
2924  * - LGPL
2925  *
2926  * image
2927  * 
2928  */
2929
2930
2931 /**
2932  * @class Roo.bootstrap.Link
2933  * @extends Roo.bootstrap.Component
2934  * Bootstrap Link Class
2935  * @cfg {String} alt image alternative text
2936  * @cfg {String} href a tag href
2937  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2938  * @cfg {String} html the content of the link.
2939  * @cfg {String} anchor name for the anchor link
2940  * @cfg {String} fa - favicon
2941
2942  * @cfg {Boolean} preventDefault (true | false) default false
2943
2944  * 
2945  * @constructor
2946  * Create a new Input
2947  * @param {Object} config The config object
2948  */
2949
2950 Roo.bootstrap.Link = function(config){
2951     Roo.bootstrap.Link.superclass.constructor.call(this, config);
2952     
2953     this.addEvents({
2954         // img events
2955         /**
2956          * @event click
2957          * The img click event for the img.
2958          * @param {Roo.EventObject} e
2959          */
2960         "click" : true
2961     });
2962 };
2963
2964 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
2965     
2966     href: false,
2967     target: false,
2968     preventDefault: false,
2969     anchor : false,
2970     alt : false,
2971     fa: false,
2972
2973
2974     getAutoCreate : function()
2975     {
2976         var html = this.html || '';
2977         
2978         if (this.fa !== false) {
2979             html = '<i class="fa fa-' + this.fa + '"></i>';
2980         }
2981         var cfg = {
2982             tag: 'a'
2983         };
2984         // anchor's do not require html/href...
2985         if (this.anchor === false) {
2986             cfg.html = html;
2987             cfg.href = this.href || '#';
2988         } else {
2989             cfg.name = this.anchor;
2990             if (this.html !== false || this.fa !== false) {
2991                 cfg.html = html;
2992             }
2993             if (this.href !== false) {
2994                 cfg.href = this.href;
2995             }
2996         }
2997         
2998         if(this.alt !== false){
2999             cfg.alt = this.alt;
3000         }
3001         
3002         
3003         if(this.target !== false) {
3004             cfg.target = this.target;
3005         }
3006         
3007         return cfg;
3008     },
3009     
3010     initEvents: function() {
3011         
3012         if(!this.href || this.preventDefault){
3013             this.el.on('click', this.onClick, this);
3014         }
3015     },
3016     
3017     onClick : function(e)
3018     {
3019         if(this.preventDefault){
3020             e.preventDefault();
3021         }
3022         //Roo.log('img onclick');
3023         this.fireEvent('click', this, e);
3024     }
3025    
3026 });
3027
3028  /*
3029  * - LGPL
3030  *
3031  * header
3032  * 
3033  */
3034
3035 /**
3036  * @class Roo.bootstrap.Header
3037  * @extends Roo.bootstrap.Component
3038  * Bootstrap Header class
3039  * @cfg {String} html content of header
3040  * @cfg {Number} level (1|2|3|4|5|6) default 1
3041  * 
3042  * @constructor
3043  * Create a new Header
3044  * @param {Object} config The config object
3045  */
3046
3047
3048 Roo.bootstrap.Header  = function(config){
3049     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3050 };
3051
3052 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3053     
3054     //href : false,
3055     html : false,
3056     level : 1,
3057     
3058     
3059     
3060     getAutoCreate : function(){
3061         
3062         
3063         
3064         var cfg = {
3065             tag: 'h' + (1 *this.level),
3066             html: this.html || ''
3067         } ;
3068         
3069         return cfg;
3070     }
3071    
3072 });
3073
3074  
3075
3076  /*
3077  * Based on:
3078  * Ext JS Library 1.1.1
3079  * Copyright(c) 2006-2007, Ext JS, LLC.
3080  *
3081  * Originally Released Under LGPL - original licence link has changed is not relivant.
3082  *
3083  * Fork - LGPL
3084  * <script type="text/javascript">
3085  */
3086  
3087 /**
3088  * @class Roo.bootstrap.MenuMgr
3089  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3090  * @singleton
3091  */
3092 Roo.bootstrap.MenuMgr = function(){
3093    var menus, active, groups = {}, attached = false, lastShow = new Date();
3094
3095    // private - called when first menu is created
3096    function init(){
3097        menus = {};
3098        active = new Roo.util.MixedCollection();
3099        Roo.get(document).addKeyListener(27, function(){
3100            if(active.length > 0){
3101                hideAll();
3102            }
3103        });
3104    }
3105
3106    // private
3107    function hideAll(){
3108        if(active && active.length > 0){
3109            var c = active.clone();
3110            c.each(function(m){
3111                m.hide();
3112            });
3113        }
3114    }
3115
3116    // private
3117    function onHide(m){
3118        active.remove(m);
3119        if(active.length < 1){
3120            Roo.get(document).un("mouseup", onMouseDown);
3121             
3122            attached = false;
3123        }
3124    }
3125
3126    // private
3127    function onShow(m){
3128        var last = active.last();
3129        lastShow = new Date();
3130        active.add(m);
3131        if(!attached){
3132           Roo.get(document).on("mouseup", onMouseDown);
3133            
3134            attached = true;
3135        }
3136        if(m.parentMenu){
3137           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3138           m.parentMenu.activeChild = m;
3139        }else if(last && last.isVisible()){
3140           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3141        }
3142    }
3143
3144    // private
3145    function onBeforeHide(m){
3146        if(m.activeChild){
3147            m.activeChild.hide();
3148        }
3149        if(m.autoHideTimer){
3150            clearTimeout(m.autoHideTimer);
3151            delete m.autoHideTimer;
3152        }
3153    }
3154
3155    // private
3156    function onBeforeShow(m){
3157        var pm = m.parentMenu;
3158        if(!pm && !m.allowOtherMenus){
3159            hideAll();
3160        }else if(pm && pm.activeChild && active != m){
3161            pm.activeChild.hide();
3162        }
3163    }
3164
3165    // private this should really trigger on mouseup..
3166    function onMouseDown(e){
3167         Roo.log("on Mouse Up");
3168         
3169         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3170             Roo.log("MenuManager hideAll");
3171             hideAll();
3172             e.stopEvent();
3173         }
3174         
3175         
3176    }
3177
3178    // private
3179    function onBeforeCheck(mi, state){
3180        if(state){
3181            var g = groups[mi.group];
3182            for(var i = 0, l = g.length; i < l; i++){
3183                if(g[i] != mi){
3184                    g[i].setChecked(false);
3185                }
3186            }
3187        }
3188    }
3189
3190    return {
3191
3192        /**
3193         * Hides all menus that are currently visible
3194         */
3195        hideAll : function(){
3196             hideAll();  
3197        },
3198
3199        // private
3200        register : function(menu){
3201            if(!menus){
3202                init();
3203            }
3204            menus[menu.id] = menu;
3205            menu.on("beforehide", onBeforeHide);
3206            menu.on("hide", onHide);
3207            menu.on("beforeshow", onBeforeShow);
3208            menu.on("show", onShow);
3209            var g = menu.group;
3210            if(g && menu.events["checkchange"]){
3211                if(!groups[g]){
3212                    groups[g] = [];
3213                }
3214                groups[g].push(menu);
3215                menu.on("checkchange", onCheck);
3216            }
3217        },
3218
3219         /**
3220          * Returns a {@link Roo.menu.Menu} object
3221          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3222          * be used to generate and return a new Menu instance.
3223          */
3224        get : function(menu){
3225            if(typeof menu == "string"){ // menu id
3226                return menus[menu];
3227            }else if(menu.events){  // menu instance
3228                return menu;
3229            }
3230            /*else if(typeof menu.length == 'number'){ // array of menu items?
3231                return new Roo.bootstrap.Menu({items:menu});
3232            }else{ // otherwise, must be a config
3233                return new Roo.bootstrap.Menu(menu);
3234            }
3235            */
3236            return false;
3237        },
3238
3239        // private
3240        unregister : function(menu){
3241            delete menus[menu.id];
3242            menu.un("beforehide", onBeforeHide);
3243            menu.un("hide", onHide);
3244            menu.un("beforeshow", onBeforeShow);
3245            menu.un("show", onShow);
3246            var g = menu.group;
3247            if(g && menu.events["checkchange"]){
3248                groups[g].remove(menu);
3249                menu.un("checkchange", onCheck);
3250            }
3251        },
3252
3253        // private
3254        registerCheckable : function(menuItem){
3255            var g = menuItem.group;
3256            if(g){
3257                if(!groups[g]){
3258                    groups[g] = [];
3259                }
3260                groups[g].push(menuItem);
3261                menuItem.on("beforecheckchange", onBeforeCheck);
3262            }
3263        },
3264
3265        // private
3266        unregisterCheckable : function(menuItem){
3267            var g = menuItem.group;
3268            if(g){
3269                groups[g].remove(menuItem);
3270                menuItem.un("beforecheckchange", onBeforeCheck);
3271            }
3272        }
3273    };
3274 }();/*
3275  * - LGPL
3276  *
3277  * menu
3278  * 
3279  */
3280
3281 /**
3282  * @class Roo.bootstrap.Menu
3283  * @extends Roo.bootstrap.Component
3284  * Bootstrap Menu class - container for MenuItems
3285  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3286  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3287  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3288  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3289  * 
3290  * @constructor
3291  * Create a new Menu
3292  * @param {Object} config The config object
3293  */
3294
3295
3296 Roo.bootstrap.Menu = function(config){
3297     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3298     if (this.registerMenu && this.type != 'treeview')  {
3299         Roo.bootstrap.MenuMgr.register(this);
3300     }
3301     
3302     
3303     this.addEvents({
3304         /**
3305          * @event beforeshow
3306          * Fires before this menu is displayed (return false to block)
3307          * @param {Roo.menu.Menu} this
3308          */
3309         beforeshow : true,
3310         /**
3311          * @event beforehide
3312          * Fires before this menu is hidden (return false to block)
3313          * @param {Roo.menu.Menu} this
3314          */
3315         beforehide : true,
3316         /**
3317          * @event show
3318          * Fires after this menu is displayed
3319          * @param {Roo.menu.Menu} this
3320          */
3321         show : true,
3322         /**
3323          * @event hide
3324          * Fires after this menu is hidden
3325          * @param {Roo.menu.Menu} this
3326          */
3327         hide : true,
3328         /**
3329          * @event click
3330          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3331          * @param {Roo.menu.Menu} this
3332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3333          * @param {Roo.EventObject} e
3334          */
3335         click : true,
3336         /**
3337          * @event mouseover
3338          * Fires when the mouse is hovering over this menu
3339          * @param {Roo.menu.Menu} this
3340          * @param {Roo.EventObject} e
3341          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3342          */
3343         mouseover : true,
3344         /**
3345          * @event mouseout
3346          * Fires when the mouse exits this menu
3347          * @param {Roo.menu.Menu} this
3348          * @param {Roo.EventObject} e
3349          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3350          */
3351         mouseout : true,
3352         /**
3353          * @event itemclick
3354          * Fires when a menu item contained in this menu is clicked
3355          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3356          * @param {Roo.EventObject} e
3357          */
3358         itemclick: true
3359     });
3360     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3361 };
3362
3363 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3364     
3365    /// html : false,
3366     //align : '',
3367     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3368     type: false,
3369     /**
3370      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3371      */
3372     registerMenu : true,
3373     
3374     menuItems :false, // stores the menu items..
3375     
3376     hidden:true,
3377         
3378     parentMenu : false,
3379     
3380     stopEvent : true,
3381     
3382     isLink : false,
3383     
3384     getChildContainer : function() {
3385         return this.el;  
3386     },
3387     
3388     getAutoCreate : function(){
3389          
3390         //if (['right'].indexOf(this.align)!==-1) {
3391         //    cfg.cn[1].cls += ' pull-right'
3392         //}
3393         
3394         
3395         var cfg = {
3396             tag : 'ul',
3397             cls : 'dropdown-menu' ,
3398             style : 'z-index:1000'
3399             
3400         };
3401         
3402         if (this.type === 'submenu') {
3403             cfg.cls = 'submenu active';
3404         }
3405         if (this.type === 'treeview') {
3406             cfg.cls = 'treeview-menu';
3407         }
3408         
3409         return cfg;
3410     },
3411     initEvents : function() {
3412         
3413        // Roo.log("ADD event");
3414        // Roo.log(this.triggerEl.dom);
3415         
3416         this.triggerEl.on('click', this.onTriggerClick, this);
3417         
3418         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3419         
3420         
3421         if (this.triggerEl.hasClass('nav-item')) {
3422             // dropdown toggle on the 'a' in BS4?
3423             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3424         } else {
3425             this.triggerEl.addClass('dropdown-toggle');
3426         }
3427         if (Roo.isTouch) {
3428             this.el.on('touchstart'  , this.onTouch, this);
3429         }
3430         this.el.on('click' , this.onClick, this);
3431
3432         this.el.on("mouseover", this.onMouseOver, this);
3433         this.el.on("mouseout", this.onMouseOut, this);
3434         
3435     },
3436     
3437     findTargetItem : function(e)
3438     {
3439         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3440         if(!t){
3441             return false;
3442         }
3443         //Roo.log(t);         Roo.log(t.id);
3444         if(t && t.id){
3445             //Roo.log(this.menuitems);
3446             return this.menuitems.get(t.id);
3447             
3448             //return this.items.get(t.menuItemId);
3449         }
3450         
3451         return false;
3452     },
3453     
3454     onTouch : function(e) 
3455     {
3456         Roo.log("menu.onTouch");
3457         //e.stopEvent(); this make the user popdown broken
3458         this.onClick(e);
3459     },
3460     
3461     onClick : function(e)
3462     {
3463         Roo.log("menu.onClick");
3464         
3465         var t = this.findTargetItem(e);
3466         if(!t || t.isContainer){
3467             return;
3468         }
3469         Roo.log(e);
3470         /*
3471         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3472             if(t == this.activeItem && t.shouldDeactivate(e)){
3473                 this.activeItem.deactivate();
3474                 delete this.activeItem;
3475                 return;
3476             }
3477             if(t.canActivate){
3478                 this.setActiveItem(t, true);
3479             }
3480             return;
3481             
3482             
3483         }
3484         */
3485        
3486         Roo.log('pass click event');
3487         
3488         t.onClick(e);
3489         
3490         this.fireEvent("click", this, t, e);
3491         
3492         var _this = this;
3493         
3494         if(!t.href.length || t.href == '#'){
3495             (function() { _this.hide(); }).defer(100);
3496         }
3497         
3498     },
3499     
3500     onMouseOver : function(e){
3501         var t  = this.findTargetItem(e);
3502         //Roo.log(t);
3503         //if(t){
3504         //    if(t.canActivate && !t.disabled){
3505         //        this.setActiveItem(t, true);
3506         //    }
3507         //}
3508         
3509         this.fireEvent("mouseover", this, e, t);
3510     },
3511     isVisible : function(){
3512         return !this.hidden;
3513     },
3514     onMouseOut : function(e){
3515         var t  = this.findTargetItem(e);
3516         
3517         //if(t ){
3518         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3519         //        this.activeItem.deactivate();
3520         //        delete this.activeItem;
3521         //    }
3522         //}
3523         this.fireEvent("mouseout", this, e, t);
3524     },
3525     
3526     
3527     /**
3528      * Displays this menu relative to another element
3529      * @param {String/HTMLElement/Roo.Element} element The element to align to
3530      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3531      * the element (defaults to this.defaultAlign)
3532      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3533      */
3534     show : function(el, pos, parentMenu)
3535     {
3536         if (false === this.fireEvent("beforeshow", this)) {
3537             Roo.log("show canceled");
3538             return;
3539         }
3540         this.parentMenu = parentMenu;
3541         if(!this.el){
3542             this.render();
3543         }
3544         
3545         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3546     },
3547      /**
3548      * Displays this menu at a specific xy position
3549      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3550      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3551      */
3552     showAt : function(xy, parentMenu, /* private: */_e){
3553         this.parentMenu = parentMenu;
3554         if(!this.el){
3555             this.render();
3556         }
3557         if(_e !== false){
3558             this.fireEvent("beforeshow", this);
3559             //xy = this.el.adjustForConstraints(xy);
3560         }
3561         
3562         //this.el.show();
3563         this.hideMenuItems();
3564         this.hidden = false;
3565         this.triggerEl.addClass('open');
3566         this.el.addClass('show');
3567         
3568         // reassign x when hitting right
3569         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3570             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3571         }
3572         
3573         // reassign y when hitting bottom
3574         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3575             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3576         }
3577         
3578         // but the list may align on trigger left or trigger top... should it be a properity?
3579         
3580         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3581             this.el.setXY(xy);
3582         }
3583         
3584         this.focus();
3585         this.fireEvent("show", this);
3586     },
3587     
3588     focus : function(){
3589         return;
3590         if(!this.hidden){
3591             this.doFocus.defer(50, this);
3592         }
3593     },
3594
3595     doFocus : function(){
3596         if(!this.hidden){
3597             this.focusEl.focus();
3598         }
3599     },
3600
3601     /**
3602      * Hides this menu and optionally all parent menus
3603      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3604      */
3605     hide : function(deep)
3606     {
3607         if (false === this.fireEvent("beforehide", this)) {
3608             Roo.log("hide canceled");
3609             return;
3610         }
3611         this.hideMenuItems();
3612         if(this.el && this.isVisible()){
3613            
3614             if(this.activeItem){
3615                 this.activeItem.deactivate();
3616                 this.activeItem = null;
3617             }
3618             this.triggerEl.removeClass('open');;
3619             this.el.removeClass('show');
3620             this.hidden = true;
3621             this.fireEvent("hide", this);
3622         }
3623         if(deep === true && this.parentMenu){
3624             this.parentMenu.hide(true);
3625         }
3626     },
3627     
3628     onTriggerClick : function(e)
3629     {
3630         Roo.log('trigger click');
3631         
3632         var target = e.getTarget();
3633         
3634         Roo.log(target.nodeName.toLowerCase());
3635         
3636         if(target.nodeName.toLowerCase() === 'i'){
3637             e.preventDefault();
3638         }
3639         
3640     },
3641     
3642     onTriggerPress  : function(e)
3643     {
3644         Roo.log('trigger press');
3645         //Roo.log(e.getTarget());
3646        // Roo.log(this.triggerEl.dom);
3647        
3648         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3649         var pel = Roo.get(e.getTarget());
3650         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3651             Roo.log('is treeview or dropdown?');
3652             return;
3653         }
3654         
3655         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3656             return;
3657         }
3658         
3659         if (this.isVisible()) {
3660             Roo.log('hide');
3661             this.hide();
3662         } else {
3663             Roo.log('show');
3664             this.show(this.triggerEl, '?', false);
3665         }
3666         
3667         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3668             e.stopEvent();
3669         }
3670         
3671     },
3672        
3673     
3674     hideMenuItems : function()
3675     {
3676         Roo.log("hide Menu Items");
3677         if (!this.el) { 
3678             return;
3679         }
3680         
3681         this.el.select('.open',true).each(function(aa) {
3682             
3683             aa.removeClass('open');
3684          
3685         });
3686     },
3687     addxtypeChild : function (tree, cntr) {
3688         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3689           
3690         this.menuitems.add(comp);
3691         return comp;
3692
3693     },
3694     getEl : function()
3695     {
3696         Roo.log(this.el);
3697         return this.el;
3698     },
3699     
3700     clear : function()
3701     {
3702         this.getEl().dom.innerHTML = '';
3703         this.menuitems.clear();
3704     }
3705 });
3706
3707  
3708  /*
3709  * - LGPL
3710  *
3711  * menu item
3712  * 
3713  */
3714
3715
3716 /**
3717  * @class Roo.bootstrap.MenuItem
3718  * @extends Roo.bootstrap.Component
3719  * Bootstrap MenuItem class
3720  * @cfg {String} html the menu label
3721  * @cfg {String} href the link
3722  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3723  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3724  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3725  * @cfg {String} fa favicon to show on left of menu item.
3726  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3727  * 
3728  * 
3729  * @constructor
3730  * Create a new MenuItem
3731  * @param {Object} config The config object
3732  */
3733
3734
3735 Roo.bootstrap.MenuItem = function(config){
3736     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3737     this.addEvents({
3738         // raw events
3739         /**
3740          * @event click
3741          * The raw click event for the entire grid.
3742          * @param {Roo.bootstrap.MenuItem} this
3743          * @param {Roo.EventObject} e
3744          */
3745         "click" : true
3746     });
3747 };
3748
3749 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3750     
3751     href : false,
3752     html : false,
3753     preventDefault: false,
3754     isContainer : false,
3755     active : false,
3756     fa: false,
3757     
3758     getAutoCreate : function(){
3759         
3760         if(this.isContainer){
3761             return {
3762                 tag: 'li',
3763                 cls: 'dropdown-menu-item '
3764             };
3765         }
3766         var ctag = {
3767             tag: 'span',
3768             html: 'Link'
3769         };
3770         
3771         var anc = {
3772             tag : 'a',
3773             cls : 'dropdown-item',
3774             href : '#',
3775             cn : [  ]
3776         };
3777         
3778         if (this.fa !== false) {
3779             anc.cn.push({
3780                 tag : 'i',
3781                 cls : 'fa fa-' + this.fa
3782             });
3783         }
3784         
3785         anc.cn.push(ctag);
3786         
3787         
3788         var cfg= {
3789             tag: 'li',
3790             cls: 'dropdown-menu-item',
3791             cn: [ anc ]
3792         };
3793         if (this.parent().type == 'treeview') {
3794             cfg.cls = 'treeview-menu';
3795         }
3796         if (this.active) {
3797             cfg.cls += ' active';
3798         }
3799         
3800         
3801         
3802         anc.href = this.href || cfg.cn[0].href ;
3803         ctag.html = this.html || cfg.cn[0].html ;
3804         return cfg;
3805     },
3806     
3807     initEvents: function()
3808     {
3809         if (this.parent().type == 'treeview') {
3810             this.el.select('a').on('click', this.onClick, this);
3811         }
3812         
3813         if (this.menu) {
3814             this.menu.parentType = this.xtype;
3815             this.menu.triggerEl = this.el;
3816             this.menu = this.addxtype(Roo.apply({}, this.menu));
3817         }
3818         
3819     },
3820     onClick : function(e)
3821     {
3822         Roo.log('item on click ');
3823         
3824         if(this.preventDefault){
3825             e.preventDefault();
3826         }
3827         //this.parent().hideMenuItems();
3828         
3829         this.fireEvent('click', this, e);
3830     },
3831     getEl : function()
3832     {
3833         return this.el;
3834     } 
3835 });
3836
3837  
3838
3839  /*
3840  * - LGPL
3841  *
3842  * menu separator
3843  * 
3844  */
3845
3846
3847 /**
3848  * @class Roo.bootstrap.MenuSeparator
3849  * @extends Roo.bootstrap.Component
3850  * Bootstrap MenuSeparator class
3851  * 
3852  * @constructor
3853  * Create a new MenuItem
3854  * @param {Object} config The config object
3855  */
3856
3857
3858 Roo.bootstrap.MenuSeparator = function(config){
3859     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3860 };
3861
3862 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3863     
3864     getAutoCreate : function(){
3865         var cfg = {
3866             cls: 'divider',
3867             tag : 'li'
3868         };
3869         
3870         return cfg;
3871     }
3872    
3873 });
3874
3875  
3876
3877  
3878 /*
3879 * Licence: LGPL
3880 */
3881
3882 /**
3883  * @class Roo.bootstrap.Modal
3884  * @extends Roo.bootstrap.Component
3885  * Bootstrap Modal class
3886  * @cfg {String} title Title of dialog
3887  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3888  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3889  * @cfg {Boolean} specificTitle default false
3890  * @cfg {Array} buttons Array of buttons or standard button set..
3891  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3892  * @cfg {Boolean} animate default true
3893  * @cfg {Boolean} allow_close default true
3894  * @cfg {Boolean} fitwindow default false
3895  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3896  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3897  * @cfg {String} size (sm|lg|xl) default empty
3898  * @cfg {Number} max_width set the max width of modal
3899  * @cfg {Boolean} editableTitle can the title be edited
3900
3901  *
3902  *
3903  * @constructor
3904  * Create a new Modal Dialog
3905  * @param {Object} config The config object
3906  */
3907
3908 Roo.bootstrap.Modal = function(config){
3909     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3910     this.addEvents({
3911         // raw events
3912         /**
3913          * @event btnclick
3914          * The raw btnclick event for the button
3915          * @param {Roo.EventObject} e
3916          */
3917         "btnclick" : true,
3918         /**
3919          * @event resize
3920          * Fire when dialog resize
3921          * @param {Roo.bootstrap.Modal} this
3922          * @param {Roo.EventObject} e
3923          */
3924         "resize" : true,
3925         /**
3926          * @event titlechanged
3927          * Fire when the editable title has been changed
3928          * @param {Roo.bootstrap.Modal} this
3929          * @param {Roo.EventObject} value
3930          */
3931         "titlechanged" : true 
3932         
3933     });
3934     this.buttons = this.buttons || [];
3935
3936     if (this.tmpl) {
3937         this.tmpl = Roo.factory(this.tmpl);
3938     }
3939
3940 };
3941
3942 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3943
3944     title : 'test dialog',
3945
3946     buttons : false,
3947
3948     // set on load...
3949
3950     html: false,
3951
3952     tmp: false,
3953
3954     specificTitle: false,
3955
3956     buttonPosition: 'right',
3957
3958     allow_close : true,
3959
3960     animate : true,
3961
3962     fitwindow: false,
3963     
3964      // private
3965     dialogEl: false,
3966     bodyEl:  false,
3967     footerEl:  false,
3968     titleEl:  false,
3969     closeEl:  false,
3970
3971     size: '',
3972     
3973     max_width: 0,
3974     
3975     max_height: 0,
3976     
3977     fit_content: false,
3978     editableTitle  : false,
3979
3980     onRender : function(ct, position)
3981     {
3982         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
3983
3984         if(!this.el){
3985             var cfg = Roo.apply({},  this.getAutoCreate());
3986             cfg.id = Roo.id();
3987             //if(!cfg.name){
3988             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
3989             //}
3990             //if (!cfg.name.length) {
3991             //    delete cfg.name;
3992            // }
3993             if (this.cls) {
3994                 cfg.cls += ' ' + this.cls;
3995             }
3996             if (this.style) {
3997                 cfg.style = this.style;
3998             }
3999             this.el = Roo.get(document.body).createChild(cfg, position);
4000         }
4001         //var type = this.el.dom.type;
4002
4003
4004         if(this.tabIndex !== undefined){
4005             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4006         }
4007
4008         this.dialogEl = this.el.select('.modal-dialog',true).first();
4009         this.bodyEl = this.el.select('.modal-body',true).first();
4010         this.closeEl = this.el.select('.modal-header .close', true).first();
4011         this.headerEl = this.el.select('.modal-header',true).first();
4012         this.titleEl = this.el.select('.modal-title',true).first();
4013         this.footerEl = this.el.select('.modal-footer',true).first();
4014
4015         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4016         
4017         //this.el.addClass("x-dlg-modal");
4018
4019         if (this.buttons.length) {
4020             Roo.each(this.buttons, function(bb) {
4021                 var b = Roo.apply({}, bb);
4022                 b.xns = b.xns || Roo.bootstrap;
4023                 b.xtype = b.xtype || 'Button';
4024                 if (typeof(b.listeners) == 'undefined') {
4025                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4026                 }
4027
4028                 var btn = Roo.factory(b);
4029
4030                 btn.render(this.getButtonContainer());
4031
4032             },this);
4033         }
4034         // render the children.
4035         var nitems = [];
4036
4037         if(typeof(this.items) != 'undefined'){
4038             var items = this.items;
4039             delete this.items;
4040
4041             for(var i =0;i < items.length;i++) {
4042                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4043             }
4044         }
4045
4046         this.items = nitems;
4047
4048         // where are these used - they used to be body/close/footer
4049
4050
4051         this.initEvents();
4052         //this.el.addClass([this.fieldClass, this.cls]);
4053
4054     },
4055
4056     getAutoCreate : function()
4057     {
4058         // we will default to modal-body-overflow - might need to remove or make optional later.
4059         var bdy = {
4060                 cls : 'modal-body enable-modal-body-overflow ', 
4061                 html : this.html || ''
4062         };
4063
4064         var title = {
4065             tag: 'h4',
4066             cls : 'modal-title',
4067             html : this.title
4068         };
4069
4070         if(this.specificTitle){ // WTF is this?
4071             title = this.title;
4072         }
4073
4074         var header = [];
4075         if (this.allow_close && Roo.bootstrap.version == 3) {
4076             header.push({
4077                 tag: 'button',
4078                 cls : 'close',
4079                 html : '&times'
4080             });
4081         }
4082
4083         header.push(title);
4084
4085         if (this.editableTitle) {
4086             header.push({
4087                 cls: 'form-control roo-editable-title d-none',
4088                 tag: 'input',
4089                 type: 'text'
4090             });
4091         }
4092         
4093         if (this.allow_close && Roo.bootstrap.version == 4) {
4094             header.push({
4095                 tag: 'button',
4096                 cls : 'close',
4097                 html : '&times'
4098             });
4099         }
4100         
4101         var size = '';
4102
4103         if(this.size.length){
4104             size = 'modal-' + this.size;
4105         }
4106         
4107         var footer = Roo.bootstrap.version == 3 ?
4108             {
4109                 cls : 'modal-footer',
4110                 cn : [
4111                     {
4112                         tag: 'div',
4113                         cls: 'btn-' + this.buttonPosition
4114                     }
4115                 ]
4116
4117             } :
4118             {  // BS4 uses mr-auto on left buttons....
4119                 cls : 'modal-footer'
4120             };
4121
4122             
4123
4124         
4125         
4126         var modal = {
4127             cls: "modal",
4128              cn : [
4129                 {
4130                     cls: "modal-dialog " + size,
4131                     cn : [
4132                         {
4133                             cls : "modal-content",
4134                             cn : [
4135                                 {
4136                                     cls : 'modal-header',
4137                                     cn : header
4138                                 },
4139                                 bdy,
4140                                 footer
4141                             ]
4142
4143                         }
4144                     ]
4145
4146                 }
4147             ]
4148         };
4149
4150         if(this.animate){
4151             modal.cls += ' fade';
4152         }
4153
4154         return modal;
4155
4156     },
4157     getChildContainer : function() {
4158
4159          return this.bodyEl;
4160
4161     },
4162     getButtonContainer : function() {
4163         
4164          return Roo.bootstrap.version == 4 ?
4165             this.el.select('.modal-footer',true).first()
4166             : this.el.select('.modal-footer div',true).first();
4167
4168     },
4169     initEvents : function()
4170     {
4171         if (this.allow_close) {
4172             this.closeEl.on('click', this.hide, this);
4173         }
4174         Roo.EventManager.onWindowResize(this.resize, this, true);
4175         if (this.editableTitle) {
4176             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4177             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4178             this.headerEditEl.on('keyup', function(e) {
4179                     if(e.isNavKeyPress()){
4180                             this.toggleHeaderInput(false)
4181                     }
4182                 }, this);
4183             this.headerEditEl.on('blur', function(e) {
4184                 this.toggleHeaderInput(false)
4185             },this);
4186         }
4187
4188     },
4189   
4190
4191     resize : function()
4192     {
4193         this.maskEl.setSize(
4194             Roo.lib.Dom.getViewWidth(true),
4195             Roo.lib.Dom.getViewHeight(true)
4196         );
4197         
4198         if (this.fitwindow) {
4199             
4200            
4201             this.setSize(
4202                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4203                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4204             );
4205             return;
4206         }
4207         
4208         if(this.max_width !== 0) {
4209             
4210             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4211             
4212             if(this.height) {
4213                 this.setSize(w, this.height);
4214                 return;
4215             }
4216             
4217             if(this.max_height) {
4218                 this.setSize(w,Math.min(
4219                     this.max_height,
4220                     Roo.lib.Dom.getViewportHeight(true) - 60
4221                 ));
4222                 
4223                 return;
4224             }
4225             
4226             if(!this.fit_content) {
4227                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4228                 return;
4229             }
4230             
4231             this.setSize(w, Math.min(
4232                 60 +
4233                 this.headerEl.getHeight() + 
4234                 this.footerEl.getHeight() + 
4235                 this.getChildHeight(this.bodyEl.dom.childNodes),
4236                 Roo.lib.Dom.getViewportHeight(true) - 60)
4237             );
4238         }
4239         
4240     },
4241
4242     setSize : function(w,h)
4243     {
4244         if (!w && !h) {
4245             return;
4246         }
4247         
4248         this.resizeTo(w,h);
4249     },
4250
4251     show : function() {
4252
4253         if (!this.rendered) {
4254             this.render();
4255         }
4256
4257         //this.el.setStyle('display', 'block');
4258         this.el.removeClass('hideing');
4259         this.el.dom.style.display='block';
4260         
4261         Roo.get(document.body).addClass('modal-open');
4262  
4263         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4264             
4265             (function(){
4266                 this.el.addClass('show');
4267                 this.el.addClass('in');
4268             }).defer(50, this);
4269         }else{
4270             this.el.addClass('show');
4271             this.el.addClass('in');
4272         }
4273
4274         // not sure how we can show data in here..
4275         //if (this.tmpl) {
4276         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4277         //}
4278
4279         Roo.get(document.body).addClass("x-body-masked");
4280         
4281         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4282         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4283         this.maskEl.dom.style.display = 'block';
4284         this.maskEl.addClass('show');
4285         
4286         
4287         this.resize();
4288         
4289         this.fireEvent('show', this);
4290
4291         // set zindex here - otherwise it appears to be ignored...
4292         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4293
4294         (function () {
4295             this.items.forEach( function(e) {
4296                 e.layout ? e.layout() : false;
4297
4298             });
4299         }).defer(100,this);
4300
4301     },
4302     hide : function()
4303     {
4304         if(this.fireEvent("beforehide", this) !== false){
4305             
4306             this.maskEl.removeClass('show');
4307             
4308             this.maskEl.dom.style.display = '';
4309             Roo.get(document.body).removeClass("x-body-masked");
4310             this.el.removeClass('in');
4311             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4312
4313             if(this.animate){ // why
4314                 this.el.addClass('hideing');
4315                 this.el.removeClass('show');
4316                 (function(){
4317                     if (!this.el.hasClass('hideing')) {
4318                         return; // it's been shown again...
4319                     }
4320                     
4321                     this.el.dom.style.display='';
4322
4323                     Roo.get(document.body).removeClass('modal-open');
4324                     this.el.removeClass('hideing');
4325                 }).defer(150,this);
4326                 
4327             }else{
4328                 this.el.removeClass('show');
4329                 this.el.dom.style.display='';
4330                 Roo.get(document.body).removeClass('modal-open');
4331
4332             }
4333             this.fireEvent('hide', this);
4334         }
4335     },
4336     isVisible : function()
4337     {
4338         
4339         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4340         
4341     },
4342
4343     addButton : function(str, cb)
4344     {
4345
4346
4347         var b = Roo.apply({}, { html : str } );
4348         b.xns = b.xns || Roo.bootstrap;
4349         b.xtype = b.xtype || 'Button';
4350         if (typeof(b.listeners) == 'undefined') {
4351             b.listeners = { click : cb.createDelegate(this)  };
4352         }
4353
4354         var btn = Roo.factory(b);
4355
4356         btn.render(this.getButtonContainer());
4357
4358         return btn;
4359
4360     },
4361
4362     setDefaultButton : function(btn)
4363     {
4364         //this.el.select('.modal-footer').()
4365     },
4366
4367     resizeTo: function(w,h)
4368     {
4369         this.dialogEl.setWidth(w);
4370         
4371         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4372
4373         this.bodyEl.setHeight(h - diff);
4374         
4375         this.fireEvent('resize', this);
4376     },
4377     
4378     setContentSize  : function(w, h)
4379     {
4380
4381     },
4382     onButtonClick: function(btn,e)
4383     {
4384         //Roo.log([a,b,c]);
4385         this.fireEvent('btnclick', btn.name, e);
4386     },
4387      /**
4388      * Set the title of the Dialog
4389      * @param {String} str new Title
4390      */
4391     setTitle: function(str) {
4392         this.titleEl.dom.innerHTML = str;
4393         this.title = str;
4394     },
4395     /**
4396      * Set the body of the Dialog
4397      * @param {String} str new Title
4398      */
4399     setBody: function(str) {
4400         this.bodyEl.dom.innerHTML = str;
4401     },
4402     /**
4403      * Set the body of the Dialog using the template
4404      * @param {Obj} data - apply this data to the template and replace the body contents.
4405      */
4406     applyBody: function(obj)
4407     {
4408         if (!this.tmpl) {
4409             Roo.log("Error - using apply Body without a template");
4410             //code
4411         }
4412         this.tmpl.overwrite(this.bodyEl, obj);
4413     },
4414     
4415     getChildHeight : function(child_nodes)
4416     {
4417         if(
4418             !child_nodes ||
4419             child_nodes.length == 0
4420         ) {
4421             return 0;
4422         }
4423         
4424         var child_height = 0;
4425         
4426         for(var i = 0; i < child_nodes.length; i++) {
4427             
4428             /*
4429             * for modal with tabs...
4430             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4431                 
4432                 var layout_childs = child_nodes[i].childNodes;
4433                 
4434                 for(var j = 0; j < layout_childs.length; j++) {
4435                     
4436                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4437                         
4438                         var layout_body_childs = layout_childs[j].childNodes;
4439                         
4440                         for(var k = 0; k < layout_body_childs.length; k++) {
4441                             
4442                             if(layout_body_childs[k].classList.contains('navbar')) {
4443                                 child_height += layout_body_childs[k].offsetHeight;
4444                                 continue;
4445                             }
4446                             
4447                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4448                                 
4449                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4450                                 
4451                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4452                                     
4453                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4454                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4455                                         continue;
4456                                     }
4457                                     
4458                                 }
4459                                 
4460                             }
4461                             
4462                         }
4463                     }
4464                 }
4465                 continue;
4466             }
4467             */
4468             
4469             child_height += child_nodes[i].offsetHeight;
4470             // Roo.log(child_nodes[i].offsetHeight);
4471         }
4472         
4473         return child_height;
4474     },
4475     toggleHeaderInput : function(is_edit)
4476     {
4477         
4478         if (is_edit && this.is_header_editing) {
4479             return; // already editing..
4480         }
4481         if (is_edit) {
4482     
4483             this.headerEditEl.dom.value = this.title;
4484             this.headerEditEl.removeClass('d-none');
4485             this.headerEditEl.dom.focus();
4486             this.titleEl.addClass('d-none');
4487             
4488             this.is_header_editing = true;
4489             return
4490         }
4491         // flip back to not editing.
4492         this.title = this.headerEditEl.dom.value;
4493         this.headerEditEl.addClass('d-none');
4494         this.titleEl.removeClass('d-none');
4495         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4496         this.is_header_editing = false;
4497         this.fireEvent('titlechanged', this, this.title);
4498     
4499             
4500         
4501     }
4502
4503 });
4504
4505
4506 Roo.apply(Roo.bootstrap.Modal,  {
4507     /**
4508          * Button config that displays a single OK button
4509          * @type Object
4510          */
4511         OK :  [{
4512             name : 'ok',
4513             weight : 'primary',
4514             html : 'OK'
4515         }],
4516         /**
4517          * Button config that displays Yes and No buttons
4518          * @type Object
4519          */
4520         YESNO : [
4521             {
4522                 name  : 'no',
4523                 html : 'No'
4524             },
4525             {
4526                 name  :'yes',
4527                 weight : 'primary',
4528                 html : 'Yes'
4529             }
4530         ],
4531
4532         /**
4533          * Button config that displays OK and Cancel buttons
4534          * @type Object
4535          */
4536         OKCANCEL : [
4537             {
4538                name : 'cancel',
4539                 html : 'Cancel'
4540             },
4541             {
4542                 name : 'ok',
4543                 weight : 'primary',
4544                 html : 'OK'
4545             }
4546         ],
4547         /**
4548          * Button config that displays Yes, No and Cancel buttons
4549          * @type Object
4550          */
4551         YESNOCANCEL : [
4552             {
4553                 name : 'yes',
4554                 weight : 'primary',
4555                 html : 'Yes'
4556             },
4557             {
4558                 name : 'no',
4559                 html : 'No'
4560             },
4561             {
4562                 name : 'cancel',
4563                 html : 'Cancel'
4564             }
4565         ],
4566         
4567         zIndex : 10001
4568 });
4569
4570 /*
4571  * - LGPL
4572  *
4573  * messagebox - can be used as a replace
4574  * 
4575  */
4576 /**
4577  * @class Roo.MessageBox
4578  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4579  * Example usage:
4580  *<pre><code>
4581 // Basic alert:
4582 Roo.Msg.alert('Status', 'Changes saved successfully.');
4583
4584 // Prompt for user data:
4585 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4586     if (btn == 'ok'){
4587         // process text value...
4588     }
4589 });
4590
4591 // Show a dialog using config options:
4592 Roo.Msg.show({
4593    title:'Save Changes?',
4594    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4595    buttons: Roo.Msg.YESNOCANCEL,
4596    fn: processResult,
4597    animEl: 'elId'
4598 });
4599 </code></pre>
4600  * @singleton
4601  */
4602 Roo.bootstrap.MessageBox = function(){
4603     var dlg, opt, mask, waitTimer;
4604     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4605     var buttons, activeTextEl, bwidth;
4606
4607     
4608     // private
4609     var handleButton = function(button){
4610         dlg.hide();
4611         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4612     };
4613
4614     // private
4615     var handleHide = function(){
4616         if(opt && opt.cls){
4617             dlg.el.removeClass(opt.cls);
4618         }
4619         //if(waitTimer){
4620         //    Roo.TaskMgr.stop(waitTimer);
4621         //    waitTimer = null;
4622         //}
4623     };
4624
4625     // private
4626     var updateButtons = function(b){
4627         var width = 0;
4628         if(!b){
4629             buttons["ok"].hide();
4630             buttons["cancel"].hide();
4631             buttons["yes"].hide();
4632             buttons["no"].hide();
4633             dlg.footerEl.hide();
4634             
4635             return width;
4636         }
4637         dlg.footerEl.show();
4638         for(var k in buttons){
4639             if(typeof buttons[k] != "function"){
4640                 if(b[k]){
4641                     buttons[k].show();
4642                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4643                     width += buttons[k].el.getWidth()+15;
4644                 }else{
4645                     buttons[k].hide();
4646                 }
4647             }
4648         }
4649         return width;
4650     };
4651
4652     // private
4653     var handleEsc = function(d, k, e){
4654         if(opt && opt.closable !== false){
4655             dlg.hide();
4656         }
4657         if(e){
4658             e.stopEvent();
4659         }
4660     };
4661
4662     return {
4663         /**
4664          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4665          * @return {Roo.BasicDialog} The BasicDialog element
4666          */
4667         getDialog : function(){
4668            if(!dlg){
4669                 dlg = new Roo.bootstrap.Modal( {
4670                     //draggable: true,
4671                     //resizable:false,
4672                     //constraintoviewport:false,
4673                     //fixedcenter:true,
4674                     //collapsible : false,
4675                     //shim:true,
4676                     //modal: true,
4677                 //    width: 'auto',
4678                   //  height:100,
4679                     //buttonAlign:"center",
4680                     closeClick : function(){
4681                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4682                             handleButton("no");
4683                         }else{
4684                             handleButton("cancel");
4685                         }
4686                     }
4687                 });
4688                 dlg.render();
4689                 dlg.on("hide", handleHide);
4690                 mask = dlg.mask;
4691                 //dlg.addKeyListener(27, handleEsc);
4692                 buttons = {};
4693                 this.buttons = buttons;
4694                 var bt = this.buttonText;
4695                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4696                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4697                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4698                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4699                 //Roo.log(buttons);
4700                 bodyEl = dlg.bodyEl.createChild({
4701
4702                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4703                         '<textarea class="roo-mb-textarea"></textarea>' +
4704                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4705                 });
4706                 msgEl = bodyEl.dom.firstChild;
4707                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4708                 textboxEl.enableDisplayMode();
4709                 textboxEl.addKeyListener([10,13], function(){
4710                     if(dlg.isVisible() && opt && opt.buttons){
4711                         if(opt.buttons.ok){
4712                             handleButton("ok");
4713                         }else if(opt.buttons.yes){
4714                             handleButton("yes");
4715                         }
4716                     }
4717                 });
4718                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4719                 textareaEl.enableDisplayMode();
4720                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4721                 progressEl.enableDisplayMode();
4722                 
4723                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4724                 var pf = progressEl.dom.firstChild;
4725                 if (pf) {
4726                     pp = Roo.get(pf.firstChild);
4727                     pp.setHeight(pf.offsetHeight);
4728                 }
4729                 
4730             }
4731             return dlg;
4732         },
4733
4734         /**
4735          * Updates the message box body text
4736          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4737          * the XHTML-compliant non-breaking space character '&amp;#160;')
4738          * @return {Roo.MessageBox} This message box
4739          */
4740         updateText : function(text)
4741         {
4742             if(!dlg.isVisible() && !opt.width){
4743                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4744                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4745             }
4746             msgEl.innerHTML = text || '&#160;';
4747       
4748             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4749             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4750             var w = Math.max(
4751                     Math.min(opt.width || cw , this.maxWidth), 
4752                     Math.max(opt.minWidth || this.minWidth, bwidth)
4753             );
4754             if(opt.prompt){
4755                 activeTextEl.setWidth(w);
4756             }
4757             if(dlg.isVisible()){
4758                 dlg.fixedcenter = false;
4759             }
4760             // to big, make it scroll. = But as usual stupid IE does not support
4761             // !important..
4762             
4763             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4764                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4765                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4766             } else {
4767                 bodyEl.dom.style.height = '';
4768                 bodyEl.dom.style.overflowY = '';
4769             }
4770             if (cw > w) {
4771                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4772             } else {
4773                 bodyEl.dom.style.overflowX = '';
4774             }
4775             
4776             dlg.setContentSize(w, bodyEl.getHeight());
4777             if(dlg.isVisible()){
4778                 dlg.fixedcenter = true;
4779             }
4780             return this;
4781         },
4782
4783         /**
4784          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4785          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4786          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4787          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4788          * @return {Roo.MessageBox} This message box
4789          */
4790         updateProgress : function(value, text){
4791             if(text){
4792                 this.updateText(text);
4793             }
4794             
4795             if (pp) { // weird bug on my firefox - for some reason this is not defined
4796                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4797                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4798             }
4799             return this;
4800         },        
4801
4802         /**
4803          * Returns true if the message box is currently displayed
4804          * @return {Boolean} True if the message box is visible, else false
4805          */
4806         isVisible : function(){
4807             return dlg && dlg.isVisible();  
4808         },
4809
4810         /**
4811          * Hides the message box if it is displayed
4812          */
4813         hide : function(){
4814             if(this.isVisible()){
4815                 dlg.hide();
4816             }  
4817         },
4818
4819         /**
4820          * Displays a new message box, or reinitializes an existing message box, based on the config options
4821          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4822          * The following config object properties are supported:
4823          * <pre>
4824 Property    Type             Description
4825 ----------  ---------------  ------------------------------------------------------------------------------------
4826 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4827                                    closes (defaults to undefined)
4828 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4829                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4830 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4831                                    progress and wait dialogs will ignore this property and always hide the
4832                                    close button as they can only be closed programmatically.
4833 cls               String           A custom CSS class to apply to the message box element
4834 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4835                                    displayed (defaults to 75)
4836 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4837                                    function will be btn (the name of the button that was clicked, if applicable,
4838                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4839                                    Progress and wait dialogs will ignore this option since they do not respond to
4840                                    user actions and can only be closed programmatically, so any required function
4841                                    should be called by the same code after it closes the dialog.
4842 icon              String           A CSS class that provides a background image to be used as an icon for
4843                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4844 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4845 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4846 modal             Boolean          False to allow user interaction with the page while the message box is
4847                                    displayed (defaults to true)
4848 msg               String           A string that will replace the existing message box body text (defaults
4849                                    to the XHTML-compliant non-breaking space character '&#160;')
4850 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4851 progress          Boolean          True to display a progress bar (defaults to false)
4852 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4853 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4854 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4855 title             String           The title text
4856 value             String           The string value to set into the active textbox element if displayed
4857 wait              Boolean          True to display a progress bar (defaults to false)
4858 width             Number           The width of the dialog in pixels
4859 </pre>
4860          *
4861          * Example usage:
4862          * <pre><code>
4863 Roo.Msg.show({
4864    title: 'Address',
4865    msg: 'Please enter your address:',
4866    width: 300,
4867    buttons: Roo.MessageBox.OKCANCEL,
4868    multiline: true,
4869    fn: saveAddress,
4870    animEl: 'addAddressBtn'
4871 });
4872 </code></pre>
4873          * @param {Object} config Configuration options
4874          * @return {Roo.MessageBox} This message box
4875          */
4876         show : function(options)
4877         {
4878             
4879             // this causes nightmares if you show one dialog after another
4880             // especially on callbacks..
4881              
4882             if(this.isVisible()){
4883                 
4884                 this.hide();
4885                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4886                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4887                 Roo.log("New Dialog Message:" +  options.msg )
4888                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4889                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4890                 
4891             }
4892             var d = this.getDialog();
4893             opt = options;
4894             d.setTitle(opt.title || "&#160;");
4895             d.closeEl.setDisplayed(opt.closable !== false);
4896             activeTextEl = textboxEl;
4897             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4898             if(opt.prompt){
4899                 if(opt.multiline){
4900                     textboxEl.hide();
4901                     textareaEl.show();
4902                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4903                         opt.multiline : this.defaultTextHeight);
4904                     activeTextEl = textareaEl;
4905                 }else{
4906                     textboxEl.show();
4907                     textareaEl.hide();
4908                 }
4909             }else{
4910                 textboxEl.hide();
4911                 textareaEl.hide();
4912             }
4913             progressEl.setDisplayed(opt.progress === true);
4914             if (opt.progress) {
4915                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4916             }
4917             this.updateProgress(0);
4918             activeTextEl.dom.value = opt.value || "";
4919             if(opt.prompt){
4920                 dlg.setDefaultButton(activeTextEl);
4921             }else{
4922                 var bs = opt.buttons;
4923                 var db = null;
4924                 if(bs && bs.ok){
4925                     db = buttons["ok"];
4926                 }else if(bs && bs.yes){
4927                     db = buttons["yes"];
4928                 }
4929                 dlg.setDefaultButton(db);
4930             }
4931             bwidth = updateButtons(opt.buttons);
4932             this.updateText(opt.msg);
4933             if(opt.cls){
4934                 d.el.addClass(opt.cls);
4935             }
4936             d.proxyDrag = opt.proxyDrag === true;
4937             d.modal = opt.modal !== false;
4938             d.mask = opt.modal !== false ? mask : false;
4939             if(!d.isVisible()){
4940                 // force it to the end of the z-index stack so it gets a cursor in FF
4941                 document.body.appendChild(dlg.el.dom);
4942                 d.animateTarget = null;
4943                 d.show(options.animEl);
4944             }
4945             return this;
4946         },
4947
4948         /**
4949          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4950          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
4951          * and closing the message box when the process is complete.
4952          * @param {String} title The title bar text
4953          * @param {String} msg The message box body text
4954          * @return {Roo.MessageBox} This message box
4955          */
4956         progress : function(title, msg){
4957             this.show({
4958                 title : title,
4959                 msg : msg,
4960                 buttons: false,
4961                 progress:true,
4962                 closable:false,
4963                 minWidth: this.minProgressWidth,
4964                 modal : true
4965             });
4966             return this;
4967         },
4968
4969         /**
4970          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
4971          * If a callback function is passed it will be called after the user clicks the button, and the
4972          * id of the button that was clicked will be passed as the only parameter to the callback
4973          * (could also be the top-right close button).
4974          * @param {String} title The title bar text
4975          * @param {String} msg The message box body text
4976          * @param {Function} fn (optional) The callback function invoked after the message box is closed
4977          * @param {Object} scope (optional) The scope of the callback function
4978          * @return {Roo.MessageBox} This message box
4979          */
4980         alert : function(title, msg, fn, scope)
4981         {
4982             this.show({
4983                 title : title,
4984                 msg : msg,
4985                 buttons: this.OK,
4986                 fn: fn,
4987                 closable : false,
4988                 scope : scope,
4989                 modal : true
4990             });
4991             return this;
4992         },
4993
4994         /**
4995          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
4996          * interaction while waiting for a long-running process to complete that does not have defined intervals.
4997          * You are responsible for closing the message box when the process is complete.
4998          * @param {String} msg The message box body text
4999          * @param {String} title (optional) The title bar text
5000          * @return {Roo.MessageBox} This message box
5001          */
5002         wait : function(msg, title){
5003             this.show({
5004                 title : title,
5005                 msg : msg,
5006                 buttons: false,
5007                 closable:false,
5008                 progress:true,
5009                 modal:true,
5010                 width:300,
5011                 wait:true
5012             });
5013             waitTimer = Roo.TaskMgr.start({
5014                 run: function(i){
5015                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5016                 },
5017                 interval: 1000
5018             });
5019             return this;
5020         },
5021
5022         /**
5023          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5024          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5025          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5026          * @param {String} title The title bar text
5027          * @param {String} msg The message box body text
5028          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5029          * @param {Object} scope (optional) The scope of the callback function
5030          * @return {Roo.MessageBox} This message box
5031          */
5032         confirm : function(title, msg, fn, scope){
5033             this.show({
5034                 title : title,
5035                 msg : msg,
5036                 buttons: this.YESNO,
5037                 fn: fn,
5038                 scope : scope,
5039                 modal : true
5040             });
5041             return this;
5042         },
5043
5044         /**
5045          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5046          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5047          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5048          * (could also be the top-right close button) and the text that was entered will be passed as the two
5049          * parameters to the callback.
5050          * @param {String} title The title bar text
5051          * @param {String} msg The message box body text
5052          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5053          * @param {Object} scope (optional) The scope of the callback function
5054          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5055          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5056          * @return {Roo.MessageBox} This message box
5057          */
5058         prompt : function(title, msg, fn, scope, multiline){
5059             this.show({
5060                 title : title,
5061                 msg : msg,
5062                 buttons: this.OKCANCEL,
5063                 fn: fn,
5064                 minWidth:250,
5065                 scope : scope,
5066                 prompt:true,
5067                 multiline: multiline,
5068                 modal : true
5069             });
5070             return this;
5071         },
5072
5073         /**
5074          * Button config that displays a single OK button
5075          * @type Object
5076          */
5077         OK : {ok:true},
5078         /**
5079          * Button config that displays Yes and No buttons
5080          * @type Object
5081          */
5082         YESNO : {yes:true, no:true},
5083         /**
5084          * Button config that displays OK and Cancel buttons
5085          * @type Object
5086          */
5087         OKCANCEL : {ok:true, cancel:true},
5088         /**
5089          * Button config that displays Yes, No and Cancel buttons
5090          * @type Object
5091          */
5092         YESNOCANCEL : {yes:true, no:true, cancel:true},
5093
5094         /**
5095          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5096          * @type Number
5097          */
5098         defaultTextHeight : 75,
5099         /**
5100          * The maximum width in pixels of the message box (defaults to 600)
5101          * @type Number
5102          */
5103         maxWidth : 600,
5104         /**
5105          * The minimum width in pixels of the message box (defaults to 100)
5106          * @type Number
5107          */
5108         minWidth : 100,
5109         /**
5110          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5111          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5112          * @type Number
5113          */
5114         minProgressWidth : 250,
5115         /**
5116          * An object containing the default button text strings that can be overriden for localized language support.
5117          * Supported properties are: ok, cancel, yes and no.
5118          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5119          * @type Object
5120          */
5121         buttonText : {
5122             ok : "OK",
5123             cancel : "Cancel",
5124             yes : "Yes",
5125             no : "No"
5126         }
5127     };
5128 }();
5129
5130 /**
5131  * Shorthand for {@link Roo.MessageBox}
5132  */
5133 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5134 Roo.Msg = Roo.Msg || Roo.MessageBox;
5135 /*
5136  * - LGPL
5137  *
5138  * navbar
5139  * 
5140  */
5141
5142 /**
5143  * @class Roo.bootstrap.Navbar
5144  * @extends Roo.bootstrap.Component
5145  * Bootstrap Navbar class
5146
5147  * @constructor
5148  * Create a new Navbar
5149  * @param {Object} config The config object
5150  */
5151
5152
5153 Roo.bootstrap.Navbar = function(config){
5154     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5155     this.addEvents({
5156         // raw events
5157         /**
5158          * @event beforetoggle
5159          * Fire before toggle the menu
5160          * @param {Roo.EventObject} e
5161          */
5162         "beforetoggle" : true
5163     });
5164 };
5165
5166 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5167     
5168     
5169    
5170     // private
5171     navItems : false,
5172     loadMask : false,
5173     
5174     
5175     getAutoCreate : function(){
5176         
5177         
5178         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5179         
5180     },
5181     
5182     initEvents :function ()
5183     {
5184         //Roo.log(this.el.select('.navbar-toggle',true));
5185         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5186         
5187         var mark = {
5188             tag: "div",
5189             cls:"x-dlg-mask"
5190         };
5191         
5192         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5193         
5194         var size = this.el.getSize();
5195         this.maskEl.setSize(size.width, size.height);
5196         this.maskEl.enableDisplayMode("block");
5197         this.maskEl.hide();
5198         
5199         if(this.loadMask){
5200             this.maskEl.show();
5201         }
5202     },
5203     
5204     
5205     getChildContainer : function()
5206     {
5207         if (this.el && this.el.select('.collapse').getCount()) {
5208             return this.el.select('.collapse',true).first();
5209         }
5210         
5211         return this.el;
5212     },
5213     
5214     mask : function()
5215     {
5216         this.maskEl.show();
5217     },
5218     
5219     unmask : function()
5220     {
5221         this.maskEl.hide();
5222     },
5223     onToggle : function()
5224     {
5225         
5226         if(this.fireEvent('beforetoggle', this) === false){
5227             return;
5228         }
5229         var ce = this.el.select('.navbar-collapse',true).first();
5230       
5231         if (!ce.hasClass('show')) {
5232            this.expand();
5233         } else {
5234             this.collapse();
5235         }
5236         
5237         
5238     
5239     },
5240     /**
5241      * Expand the navbar pulldown 
5242      */
5243     expand : function ()
5244     {
5245        
5246         var ce = this.el.select('.navbar-collapse',true).first();
5247         if (ce.hasClass('collapsing')) {
5248             return;
5249         }
5250         ce.dom.style.height = '';
5251                // show it...
5252         ce.addClass('in'); // old...
5253         ce.removeClass('collapse');
5254         ce.addClass('show');
5255         var h = ce.getHeight();
5256         Roo.log(h);
5257         ce.removeClass('show');
5258         // at this point we should be able to see it..
5259         ce.addClass('collapsing');
5260         
5261         ce.setHeight(0); // resize it ...
5262         ce.on('transitionend', function() {
5263             //Roo.log('done transition');
5264             ce.removeClass('collapsing');
5265             ce.addClass('show');
5266             ce.removeClass('collapse');
5267
5268             ce.dom.style.height = '';
5269         }, this, { single: true} );
5270         ce.setHeight(h);
5271         ce.dom.scrollTop = 0;
5272     },
5273     /**
5274      * Collapse the navbar pulldown 
5275      */
5276     collapse : function()
5277     {
5278          var ce = this.el.select('.navbar-collapse',true).first();
5279        
5280         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5281             // it's collapsed or collapsing..
5282             return;
5283         }
5284         ce.removeClass('in'); // old...
5285         ce.setHeight(ce.getHeight());
5286         ce.removeClass('show');
5287         ce.addClass('collapsing');
5288         
5289         ce.on('transitionend', function() {
5290             ce.dom.style.height = '';
5291             ce.removeClass('collapsing');
5292             ce.addClass('collapse');
5293         }, this, { single: true} );
5294         ce.setHeight(0);
5295     }
5296     
5297     
5298     
5299 });
5300
5301
5302
5303  
5304
5305  /*
5306  * - LGPL
5307  *
5308  * navbar
5309  * 
5310  */
5311
5312 /**
5313  * @class Roo.bootstrap.NavSimplebar
5314  * @extends Roo.bootstrap.Navbar
5315  * Bootstrap Sidebar class
5316  *
5317  * @cfg {Boolean} inverse is inverted color
5318  * 
5319  * @cfg {String} type (nav | pills | tabs)
5320  * @cfg {Boolean} arrangement stacked | justified
5321  * @cfg {String} align (left | right) alignment
5322  * 
5323  * @cfg {Boolean} main (true|false) main nav bar? default false
5324  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5325  * 
5326  * @cfg {String} tag (header|footer|nav|div) default is nav 
5327
5328  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5329  * 
5330  * 
5331  * @constructor
5332  * Create a new Sidebar
5333  * @param {Object} config The config object
5334  */
5335
5336
5337 Roo.bootstrap.NavSimplebar = function(config){
5338     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5339 };
5340
5341 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5342     
5343     inverse: false,
5344     
5345     type: false,
5346     arrangement: '',
5347     align : false,
5348     
5349     weight : 'light',
5350     
5351     main : false,
5352     
5353     
5354     tag : false,
5355     
5356     
5357     getAutoCreate : function(){
5358         
5359         
5360         var cfg = {
5361             tag : this.tag || 'div',
5362             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5363         };
5364         if (['light','white'].indexOf(this.weight) > -1) {
5365             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5366         }
5367         cfg.cls += ' bg-' + this.weight;
5368         
5369         if (this.inverse) {
5370             cfg.cls += ' navbar-inverse';
5371             
5372         }
5373         
5374         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5375         
5376         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5377             return cfg;
5378         }
5379         
5380         
5381     
5382         
5383         cfg.cn = [
5384             {
5385                 cls: 'nav nav-' + this.xtype,
5386                 tag : 'ul'
5387             }
5388         ];
5389         
5390          
5391         this.type = this.type || 'nav';
5392         if (['tabs','pills'].indexOf(this.type) != -1) {
5393             cfg.cn[0].cls += ' nav-' + this.type
5394         
5395         
5396         } else {
5397             if (this.type!=='nav') {
5398                 Roo.log('nav type must be nav/tabs/pills')
5399             }
5400             cfg.cn[0].cls += ' navbar-nav'
5401         }
5402         
5403         
5404         
5405         
5406         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5407             cfg.cn[0].cls += ' nav-' + this.arrangement;
5408         }
5409         
5410         
5411         if (this.align === 'right') {
5412             cfg.cn[0].cls += ' navbar-right';
5413         }
5414         
5415         
5416         
5417         
5418         return cfg;
5419     
5420         
5421     }
5422     
5423     
5424     
5425 });
5426
5427
5428
5429  
5430
5431  
5432        /*
5433  * - LGPL
5434  *
5435  * navbar
5436  * navbar-fixed-top
5437  * navbar-expand-md  fixed-top 
5438  */
5439
5440 /**
5441  * @class Roo.bootstrap.NavHeaderbar
5442  * @extends Roo.bootstrap.NavSimplebar
5443  * Bootstrap Sidebar class
5444  *
5445  * @cfg {String} brand what is brand
5446  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5447  * @cfg {String} brand_href href of the brand
5448  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5449  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5450  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5451  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5452  * 
5453  * @constructor
5454  * Create a new Sidebar
5455  * @param {Object} config The config object
5456  */
5457
5458
5459 Roo.bootstrap.NavHeaderbar = function(config){
5460     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5461       
5462 };
5463
5464 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5465     
5466     position: '',
5467     brand: '',
5468     brand_href: false,
5469     srButton : true,
5470     autohide : false,
5471     desktopCenter : false,
5472    
5473     
5474     getAutoCreate : function(){
5475         
5476         var   cfg = {
5477             tag: this.nav || 'nav',
5478             cls: 'navbar navbar-expand-md',
5479             role: 'navigation',
5480             cn: []
5481         };
5482         
5483         var cn = cfg.cn;
5484         if (this.desktopCenter) {
5485             cn.push({cls : 'container', cn : []});
5486             cn = cn[0].cn;
5487         }
5488         
5489         if(this.srButton){
5490             var btn = {
5491                 tag: 'button',
5492                 type: 'button',
5493                 cls: 'navbar-toggle navbar-toggler',
5494                 'data-toggle': 'collapse',
5495                 cn: [
5496                     {
5497                         tag: 'span',
5498                         cls: 'sr-only',
5499                         html: 'Toggle navigation'
5500                     },
5501                     {
5502                         tag: 'span',
5503                         cls: 'icon-bar navbar-toggler-icon'
5504                     },
5505                     {
5506                         tag: 'span',
5507                         cls: 'icon-bar'
5508                     },
5509                     {
5510                         tag: 'span',
5511                         cls: 'icon-bar'
5512                     }
5513                 ]
5514             };
5515             
5516             cn.push( Roo.bootstrap.version == 4 ? btn : {
5517                 tag: 'div',
5518                 cls: 'navbar-header',
5519                 cn: [
5520                     btn
5521                 ]
5522             });
5523         }
5524         
5525         cn.push({
5526             tag: 'div',
5527             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5528             cn : []
5529         });
5530         
5531         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5532         
5533         if (['light','white'].indexOf(this.weight) > -1) {
5534             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5535         }
5536         cfg.cls += ' bg-' + this.weight;
5537         
5538         
5539         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5540             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5541             
5542             // tag can override this..
5543             
5544             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5545         }
5546         
5547         if (this.brand !== '') {
5548             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5549             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5550                 tag: 'a',
5551                 href: this.brand_href ? this.brand_href : '#',
5552                 cls: 'navbar-brand',
5553                 cn: [
5554                 this.brand
5555                 ]
5556             });
5557         }
5558         
5559         if(this.main){
5560             cfg.cls += ' main-nav';
5561         }
5562         
5563         
5564         return cfg;
5565
5566         
5567     },
5568     getHeaderChildContainer : function()
5569     {
5570         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5571             return this.el.select('.navbar-header',true).first();
5572         }
5573         
5574         return this.getChildContainer();
5575     },
5576     
5577     getChildContainer : function()
5578     {
5579          
5580         return this.el.select('.roo-navbar-collapse',true).first();
5581          
5582         
5583     },
5584     
5585     initEvents : function()
5586     {
5587         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5588         
5589         if (this.autohide) {
5590             
5591             var prevScroll = 0;
5592             var ft = this.el;
5593             
5594             Roo.get(document).on('scroll',function(e) {
5595                 var ns = Roo.get(document).getScroll().top;
5596                 var os = prevScroll;
5597                 prevScroll = ns;
5598                 
5599                 if(ns > os){
5600                     ft.removeClass('slideDown');
5601                     ft.addClass('slideUp');
5602                     return;
5603                 }
5604                 ft.removeClass('slideUp');
5605                 ft.addClass('slideDown');
5606                  
5607               
5608           },this);
5609         }
5610     }    
5611     
5612 });
5613
5614
5615
5616  
5617
5618  /*
5619  * - LGPL
5620  *
5621  * navbar
5622  * 
5623  */
5624
5625 /**
5626  * @class Roo.bootstrap.NavSidebar
5627  * @extends Roo.bootstrap.Navbar
5628  * Bootstrap Sidebar class
5629  * 
5630  * @constructor
5631  * Create a new Sidebar
5632  * @param {Object} config The config object
5633  */
5634
5635
5636 Roo.bootstrap.NavSidebar = function(config){
5637     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5638 };
5639
5640 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5641     
5642     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5643     
5644     getAutoCreate : function(){
5645         
5646         
5647         return  {
5648             tag: 'div',
5649             cls: 'sidebar sidebar-nav'
5650         };
5651     
5652         
5653     }
5654     
5655     
5656     
5657 });
5658
5659
5660
5661  
5662
5663  /*
5664  * - LGPL
5665  *
5666  * nav group
5667  * 
5668  */
5669
5670 /**
5671  * @class Roo.bootstrap.NavGroup
5672  * @extends Roo.bootstrap.Component
5673  * Bootstrap NavGroup class
5674  * @cfg {String} align (left|right)
5675  * @cfg {Boolean} inverse
5676  * @cfg {String} type (nav|pills|tab) default nav
5677  * @cfg {String} navId - reference Id for navbar.
5678
5679  * 
5680  * @constructor
5681  * Create a new nav group
5682  * @param {Object} config The config object
5683  */
5684
5685 Roo.bootstrap.NavGroup = function(config){
5686     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5687     this.navItems = [];
5688    
5689     Roo.bootstrap.NavGroup.register(this);
5690      this.addEvents({
5691         /**
5692              * @event changed
5693              * Fires when the active item changes
5694              * @param {Roo.bootstrap.NavGroup} this
5695              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5696              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5697          */
5698         'changed': true
5699      });
5700     
5701 };
5702
5703 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5704     
5705     align: '',
5706     inverse: false,
5707     form: false,
5708     type: 'nav',
5709     navId : '',
5710     // private
5711     
5712     navItems : false, 
5713     
5714     getAutoCreate : function()
5715     {
5716         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5717         
5718         cfg = {
5719             tag : 'ul',
5720             cls: 'nav' 
5721         };
5722         if (Roo.bootstrap.version == 4) {
5723             if (['tabs','pills'].indexOf(this.type) != -1) {
5724                 cfg.cls += ' nav-' + this.type; 
5725             } else {
5726                 // trying to remove so header bar can right align top?
5727                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5728                     // do not use on header bar... 
5729                     cfg.cls += ' navbar-nav';
5730                 }
5731             }
5732             
5733         } else {
5734             if (['tabs','pills'].indexOf(this.type) != -1) {
5735                 cfg.cls += ' nav-' + this.type
5736             } else {
5737                 if (this.type !== 'nav') {
5738                     Roo.log('nav type must be nav/tabs/pills')
5739                 }
5740                 cfg.cls += ' navbar-nav'
5741             }
5742         }
5743         
5744         if (this.parent() && this.parent().sidebar) {
5745             cfg = {
5746                 tag: 'ul',
5747                 cls: 'dashboard-menu sidebar-menu'
5748             };
5749             
5750             return cfg;
5751         }
5752         
5753         if (this.form === true) {
5754             cfg = {
5755                 tag: 'form',
5756                 cls: 'navbar-form form-inline'
5757             };
5758             //nav navbar-right ml-md-auto
5759             if (this.align === 'right') {
5760                 cfg.cls += ' navbar-right ml-md-auto';
5761             } else {
5762                 cfg.cls += ' navbar-left';
5763             }
5764         }
5765         
5766         if (this.align === 'right') {
5767             cfg.cls += ' navbar-right ml-md-auto';
5768         } else {
5769             cfg.cls += ' mr-auto';
5770         }
5771         
5772         if (this.inverse) {
5773             cfg.cls += ' navbar-inverse';
5774             
5775         }
5776         
5777         
5778         return cfg;
5779     },
5780     /**
5781     * sets the active Navigation item
5782     * @param {Roo.bootstrap.NavItem} the new current navitem
5783     */
5784     setActiveItem : function(item)
5785     {
5786         var prev = false;
5787         Roo.each(this.navItems, function(v){
5788             if (v == item) {
5789                 return ;
5790             }
5791             if (v.isActive()) {
5792                 v.setActive(false, true);
5793                 prev = v;
5794                 
5795             }
5796             
5797         });
5798
5799         item.setActive(true, true);
5800         this.fireEvent('changed', this, item, prev);
5801         
5802         
5803     },
5804     /**
5805     * gets the active Navigation item
5806     * @return {Roo.bootstrap.NavItem} the current navitem
5807     */
5808     getActive : function()
5809     {
5810         
5811         var prev = false;
5812         Roo.each(this.navItems, function(v){
5813             
5814             if (v.isActive()) {
5815                 prev = v;
5816                 
5817             }
5818             
5819         });
5820         return prev;
5821     },
5822     
5823     indexOfNav : function()
5824     {
5825         
5826         var prev = false;
5827         Roo.each(this.navItems, function(v,i){
5828             
5829             if (v.isActive()) {
5830                 prev = i;
5831                 
5832             }
5833             
5834         });
5835         return prev;
5836     },
5837     /**
5838     * adds a Navigation item
5839     * @param {Roo.bootstrap.NavItem} the navitem to add
5840     */
5841     addItem : function(cfg)
5842     {
5843         if (this.form && Roo.bootstrap.version == 4) {
5844             cfg.tag = 'div';
5845         }
5846         var cn = new Roo.bootstrap.NavItem(cfg);
5847         this.register(cn);
5848         cn.parentId = this.id;
5849         cn.onRender(this.el, null);
5850         return cn;
5851     },
5852     /**
5853     * register a Navigation item
5854     * @param {Roo.bootstrap.NavItem} the navitem to add
5855     */
5856     register : function(item)
5857     {
5858         this.navItems.push( item);
5859         item.navId = this.navId;
5860     
5861     },
5862     
5863     /**
5864     * clear all the Navigation item
5865     */
5866    
5867     clearAll : function()
5868     {
5869         this.navItems = [];
5870         this.el.dom.innerHTML = '';
5871     },
5872     
5873     getNavItem: function(tabId)
5874     {
5875         var ret = false;
5876         Roo.each(this.navItems, function(e) {
5877             if (e.tabId == tabId) {
5878                ret =  e;
5879                return false;
5880             }
5881             return true;
5882             
5883         });
5884         return ret;
5885     },
5886     
5887     setActiveNext : function()
5888     {
5889         var i = this.indexOfNav(this.getActive());
5890         if (i > this.navItems.length) {
5891             return;
5892         }
5893         this.setActiveItem(this.navItems[i+1]);
5894     },
5895     setActivePrev : function()
5896     {
5897         var i = this.indexOfNav(this.getActive());
5898         if (i  < 1) {
5899             return;
5900         }
5901         this.setActiveItem(this.navItems[i-1]);
5902     },
5903     clearWasActive : function(except) {
5904         Roo.each(this.navItems, function(e) {
5905             if (e.tabId != except.tabId && e.was_active) {
5906                e.was_active = false;
5907                return false;
5908             }
5909             return true;
5910             
5911         });
5912     },
5913     getWasActive : function ()
5914     {
5915         var r = false;
5916         Roo.each(this.navItems, function(e) {
5917             if (e.was_active) {
5918                r = e;
5919                return false;
5920             }
5921             return true;
5922             
5923         });
5924         return r;
5925     }
5926     
5927     
5928 });
5929
5930  
5931 Roo.apply(Roo.bootstrap.NavGroup, {
5932     
5933     groups: {},
5934      /**
5935     * register a Navigation Group
5936     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5937     */
5938     register : function(navgrp)
5939     {
5940         this.groups[navgrp.navId] = navgrp;
5941         
5942     },
5943     /**
5944     * fetch a Navigation Group based on the navigation ID
5945     * @param {string} the navgroup to add
5946     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5947     */
5948     get: function(navId) {
5949         if (typeof(this.groups[navId]) == 'undefined') {
5950             return false;
5951             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
5952         }
5953         return this.groups[navId] ;
5954     }
5955     
5956     
5957     
5958 });
5959
5960  /*
5961  * - LGPL
5962  *
5963  * row
5964  * 
5965  */
5966
5967 /**
5968  * @class Roo.bootstrap.NavItem
5969  * @extends Roo.bootstrap.Component
5970  * Bootstrap Navbar.NavItem class
5971  * @cfg {String} href  link to
5972  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
5973
5974  * @cfg {String} html content of button
5975  * @cfg {String} badge text inside badge
5976  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
5977  * @cfg {String} glyphicon DEPRICATED - use fa
5978  * @cfg {String} icon DEPRICATED - use fa
5979  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
5980  * @cfg {Boolean} active Is item active
5981  * @cfg {Boolean} disabled Is item disabled
5982  
5983  * @cfg {Boolean} preventDefault (true | false) default false
5984  * @cfg {String} tabId the tab that this item activates.
5985  * @cfg {String} tagtype (a|span) render as a href or span?
5986  * @cfg {Boolean} animateRef (true|false) link to element default false  
5987   
5988  * @constructor
5989  * Create a new Navbar Item
5990  * @param {Object} config The config object
5991  */
5992 Roo.bootstrap.NavItem = function(config){
5993     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
5994     this.addEvents({
5995         // raw events
5996         /**
5997          * @event click
5998          * The raw click event for the entire grid.
5999          * @param {Roo.EventObject} e
6000          */
6001         "click" : true,
6002          /**
6003             * @event changed
6004             * Fires when the active item active state changes
6005             * @param {Roo.bootstrap.NavItem} this
6006             * @param {boolean} state the new state
6007              
6008          */
6009         'changed': true,
6010         /**
6011             * @event scrollto
6012             * Fires when scroll to element
6013             * @param {Roo.bootstrap.NavItem} this
6014             * @param {Object} options
6015             * @param {Roo.EventObject} e
6016              
6017          */
6018         'scrollto': true
6019     });
6020    
6021 };
6022
6023 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6024     
6025     href: false,
6026     html: '',
6027     badge: '',
6028     icon: false,
6029     fa : false,
6030     glyphicon: false,
6031     active: false,
6032     preventDefault : false,
6033     tabId : false,
6034     tagtype : 'a',
6035     tag: 'li',
6036     disabled : false,
6037     animateRef : false,
6038     was_active : false,
6039     button_weight : '',
6040     button_outline : false,
6041     
6042     navLink: false,
6043     
6044     getAutoCreate : function(){
6045          
6046         var cfg = {
6047             tag: this.tag,
6048             cls: 'nav-item'
6049         };
6050         
6051         if (this.active) {
6052             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6053         }
6054         if (this.disabled) {
6055             cfg.cls += ' disabled';
6056         }
6057         
6058         // BS4 only?
6059         if (this.button_weight.length) {
6060             cfg.tag = this.href ? 'a' : 'button';
6061             cfg.html = this.html || '';
6062             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6063             if (this.href) {
6064                 cfg.href = this.href;
6065             }
6066             if (this.fa) {
6067                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6068             }
6069             
6070             // menu .. should add dropdown-menu class - so no need for carat..
6071             
6072             if (this.badge !== '') {
6073                  
6074                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6075             }
6076             return cfg;
6077         }
6078         
6079         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6080             cfg.cn = [
6081                 {
6082                     tag: this.tagtype,
6083                     href : this.href || "#",
6084                     html: this.html || ''
6085                 }
6086             ];
6087             if (this.tagtype == 'a') {
6088                 cfg.cn[0].cls = 'nav-link';
6089             }
6090             if (this.icon) {
6091                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6092             }
6093             if (this.fa) {
6094                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6095             }
6096             if(this.glyphicon) {
6097                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6098             }
6099             
6100             if (this.menu) {
6101                 
6102                 cfg.cn[0].html += " <span class='caret'></span>";
6103              
6104             }
6105             
6106             if (this.badge !== '') {
6107                  
6108                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6109             }
6110         }
6111         
6112         
6113         
6114         return cfg;
6115     },
6116     onRender : function(ct, position)
6117     {
6118        // Roo.log("Call onRender: " + this.xtype);
6119         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6120             this.tag = 'div';
6121         }
6122         
6123         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6124         this.navLink = this.el.select('.nav-link',true).first();
6125         return ret;
6126     },
6127       
6128     
6129     initEvents: function() 
6130     {
6131         if (typeof (this.menu) != 'undefined') {
6132             this.menu.parentType = this.xtype;
6133             this.menu.triggerEl = this.el;
6134             this.menu = this.addxtype(Roo.apply({}, this.menu));
6135         }
6136         
6137         this.el.select('a',true).on('click', this.onClick, this);
6138         
6139         if(this.tagtype == 'span'){
6140             this.el.select('span',true).on('click', this.onClick, this);
6141         }
6142        
6143         // at this point parent should be available..
6144         this.parent().register(this);
6145     },
6146     
6147     onClick : function(e)
6148     {
6149         if (e.getTarget('.dropdown-menu-item')) {
6150             // did you click on a menu itemm.... - then don't trigger onclick..
6151             return;
6152         }
6153         
6154         if(
6155                 this.preventDefault || 
6156                 this.href == '#' 
6157         ){
6158             Roo.log("NavItem - prevent Default?");
6159             e.preventDefault();
6160         }
6161         
6162         if (this.disabled) {
6163             return;
6164         }
6165         
6166         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6167         if (tg && tg.transition) {
6168             Roo.log("waiting for the transitionend");
6169             return;
6170         }
6171         
6172         
6173         
6174         //Roo.log("fire event clicked");
6175         if(this.fireEvent('click', this, e) === false){
6176             return;
6177         };
6178         
6179         if(this.tagtype == 'span'){
6180             return;
6181         }
6182         
6183         //Roo.log(this.href);
6184         var ael = this.el.select('a',true).first();
6185         //Roo.log(ael);
6186         
6187         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6188             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6189             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6190                 return; // ignore... - it's a 'hash' to another page.
6191             }
6192             Roo.log("NavItem - prevent Default?");
6193             e.preventDefault();
6194             this.scrollToElement(e);
6195         }
6196         
6197         
6198         var p =  this.parent();
6199    
6200         if (['tabs','pills'].indexOf(p.type)!==-1) {
6201             if (typeof(p.setActiveItem) !== 'undefined') {
6202                 p.setActiveItem(this);
6203             }
6204         }
6205         
6206         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6207         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6208             // remove the collapsed menu expand...
6209             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6210         }
6211     },
6212     
6213     isActive: function () {
6214         return this.active
6215     },
6216     setActive : function(state, fire, is_was_active)
6217     {
6218         if (this.active && !state && this.navId) {
6219             this.was_active = true;
6220             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6221             if (nv) {
6222                 nv.clearWasActive(this);
6223             }
6224             
6225         }
6226         this.active = state;
6227         
6228         if (!state ) {
6229             this.el.removeClass('active');
6230             this.navLink ? this.navLink.removeClass('active') : false;
6231         } else if (!this.el.hasClass('active')) {
6232             
6233             this.el.addClass('active');
6234             if (Roo.bootstrap.version == 4 && this.navLink ) {
6235                 this.navLink.addClass('active');
6236             }
6237             
6238         }
6239         if (fire) {
6240             this.fireEvent('changed', this, state);
6241         }
6242         
6243         // show a panel if it's registered and related..
6244         
6245         if (!this.navId || !this.tabId || !state || is_was_active) {
6246             return;
6247         }
6248         
6249         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6250         if (!tg) {
6251             return;
6252         }
6253         var pan = tg.getPanelByName(this.tabId);
6254         if (!pan) {
6255             return;
6256         }
6257         // if we can not flip to new panel - go back to old nav highlight..
6258         if (false == tg.showPanel(pan)) {
6259             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6260             if (nv) {
6261                 var onav = nv.getWasActive();
6262                 if (onav) {
6263                     onav.setActive(true, false, true);
6264                 }
6265             }
6266             
6267         }
6268         
6269         
6270         
6271     },
6272      // this should not be here...
6273     setDisabled : function(state)
6274     {
6275         this.disabled = state;
6276         if (!state ) {
6277             this.el.removeClass('disabled');
6278         } else if (!this.el.hasClass('disabled')) {
6279             this.el.addClass('disabled');
6280         }
6281         
6282     },
6283     
6284     /**
6285      * Fetch the element to display the tooltip on.
6286      * @return {Roo.Element} defaults to this.el
6287      */
6288     tooltipEl : function()
6289     {
6290         return this.el.select('' + this.tagtype + '', true).first();
6291     },
6292     
6293     scrollToElement : function(e)
6294     {
6295         var c = document.body;
6296         
6297         /*
6298          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6299          */
6300         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6301             c = document.documentElement;
6302         }
6303         
6304         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6305         
6306         if(!target){
6307             return;
6308         }
6309
6310         var o = target.calcOffsetsTo(c);
6311         
6312         var options = {
6313             target : target,
6314             value : o[1]
6315         };
6316         
6317         this.fireEvent('scrollto', this, options, e);
6318         
6319         Roo.get(c).scrollTo('top', options.value, true);
6320         
6321         return;
6322     }
6323 });
6324  
6325
6326  /*
6327  * - LGPL
6328  *
6329  * sidebar item
6330  *
6331  *  li
6332  *    <span> icon </span>
6333  *    <span> text </span>
6334  *    <span>badge </span>
6335  */
6336
6337 /**
6338  * @class Roo.bootstrap.NavSidebarItem
6339  * @extends Roo.bootstrap.NavItem
6340  * Bootstrap Navbar.NavSidebarItem class
6341  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6342  * {Boolean} open is the menu open
6343  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6344  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6345  * {String} buttonSize (sm|md|lg)the extra classes for the button
6346  * {Boolean} showArrow show arrow next to the text (default true)
6347  * @constructor
6348  * Create a new Navbar Button
6349  * @param {Object} config The config object
6350  */
6351 Roo.bootstrap.NavSidebarItem = function(config){
6352     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6353     this.addEvents({
6354         // raw events
6355         /**
6356          * @event click
6357          * The raw click event for the entire grid.
6358          * @param {Roo.EventObject} e
6359          */
6360         "click" : true,
6361          /**
6362             * @event changed
6363             * Fires when the active item active state changes
6364             * @param {Roo.bootstrap.NavSidebarItem} this
6365             * @param {boolean} state the new state
6366              
6367          */
6368         'changed': true
6369     });
6370    
6371 };
6372
6373 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6374     
6375     badgeWeight : 'default',
6376     
6377     open: false,
6378     
6379     buttonView : false,
6380     
6381     buttonWeight : 'default',
6382     
6383     buttonSize : 'md',
6384     
6385     showArrow : true,
6386     
6387     getAutoCreate : function(){
6388         
6389         
6390         var a = {
6391                 tag: 'a',
6392                 href : this.href || '#',
6393                 cls: '',
6394                 html : '',
6395                 cn : []
6396         };
6397         
6398         if(this.buttonView){
6399             a = {
6400                 tag: 'button',
6401                 href : this.href || '#',
6402                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6403                 html : this.html,
6404                 cn : []
6405             };
6406         }
6407         
6408         var cfg = {
6409             tag: 'li',
6410             cls: '',
6411             cn: [ a ]
6412         };
6413         
6414         if (this.active) {
6415             cfg.cls += ' active';
6416         }
6417         
6418         if (this.disabled) {
6419             cfg.cls += ' disabled';
6420         }
6421         if (this.open) {
6422             cfg.cls += ' open x-open';
6423         }
6424         // left icon..
6425         if (this.glyphicon || this.icon) {
6426             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6427             a.cn.push({ tag : 'i', cls : c }) ;
6428         }
6429         
6430         if(!this.buttonView){
6431             var span = {
6432                 tag: 'span',
6433                 html : this.html || ''
6434             };
6435
6436             a.cn.push(span);
6437             
6438         }
6439         
6440         if (this.badge !== '') {
6441             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6442         }
6443         
6444         if (this.menu) {
6445             
6446             if(this.showArrow){
6447                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6448             }
6449             
6450             a.cls += ' dropdown-toggle treeview' ;
6451         }
6452         
6453         return cfg;
6454     },
6455     
6456     initEvents : function()
6457     { 
6458         if (typeof (this.menu) != 'undefined') {
6459             this.menu.parentType = this.xtype;
6460             this.menu.triggerEl = this.el;
6461             this.menu = this.addxtype(Roo.apply({}, this.menu));
6462         }
6463         
6464         this.el.on('click', this.onClick, this);
6465         
6466         if(this.badge !== ''){
6467             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6468         }
6469         
6470     },
6471     
6472     onClick : function(e)
6473     {
6474         if(this.disabled){
6475             e.preventDefault();
6476             return;
6477         }
6478         
6479         if(this.preventDefault){
6480             e.preventDefault();
6481         }
6482         
6483         this.fireEvent('click', this, e);
6484     },
6485     
6486     disable : function()
6487     {
6488         this.setDisabled(true);
6489     },
6490     
6491     enable : function()
6492     {
6493         this.setDisabled(false);
6494     },
6495     
6496     setDisabled : function(state)
6497     {
6498         if(this.disabled == state){
6499             return;
6500         }
6501         
6502         this.disabled = state;
6503         
6504         if (state) {
6505             this.el.addClass('disabled');
6506             return;
6507         }
6508         
6509         this.el.removeClass('disabled');
6510         
6511         return;
6512     },
6513     
6514     setActive : function(state)
6515     {
6516         if(this.active == state){
6517             return;
6518         }
6519         
6520         this.active = state;
6521         
6522         if (state) {
6523             this.el.addClass('active');
6524             return;
6525         }
6526         
6527         this.el.removeClass('active');
6528         
6529         return;
6530     },
6531     
6532     isActive: function () 
6533     {
6534         return this.active;
6535     },
6536     
6537     setBadge : function(str)
6538     {
6539         if(!this.badgeEl){
6540             return;
6541         }
6542         
6543         this.badgeEl.dom.innerHTML = str;
6544     }
6545     
6546    
6547      
6548  
6549 });
6550  
6551
6552  /*
6553  * - LGPL
6554  *
6555  * row
6556  * 
6557  */
6558
6559 /**
6560  * @class Roo.bootstrap.Row
6561  * @extends Roo.bootstrap.Component
6562  * Bootstrap Row class (contains columns...)
6563  * 
6564  * @constructor
6565  * Create a new Row
6566  * @param {Object} config The config object
6567  */
6568
6569 Roo.bootstrap.Row = function(config){
6570     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6571 };
6572
6573 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6574     
6575     getAutoCreate : function(){
6576        return {
6577             cls: 'row clearfix'
6578        };
6579     }
6580     
6581     
6582 });
6583
6584  
6585
6586  /*
6587  * - LGPL
6588  *
6589  * pagination
6590  * 
6591  */
6592
6593 /**
6594  * @class Roo.bootstrap.Pagination
6595  * @extends Roo.bootstrap.Component
6596  * Bootstrap Pagination class
6597  * @cfg {String} size xs | sm | md | lg
6598  * @cfg {Boolean} inverse false | true
6599  * 
6600  * @constructor
6601  * Create a new Pagination
6602  * @param {Object} config The config object
6603  */
6604
6605 Roo.bootstrap.Pagination = function(config){
6606     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6607 };
6608
6609 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6610     
6611     cls: false,
6612     size: false,
6613     inverse: false,
6614     
6615     getAutoCreate : function(){
6616         var cfg = {
6617             tag: 'ul',
6618                 cls: 'pagination'
6619         };
6620         if (this.inverse) {
6621             cfg.cls += ' inverse';
6622         }
6623         if (this.html) {
6624             cfg.html=this.html;
6625         }
6626         if (this.cls) {
6627             cfg.cls += " " + this.cls;
6628         }
6629         return cfg;
6630     }
6631    
6632 });
6633
6634  
6635
6636  /*
6637  * - LGPL
6638  *
6639  * Pagination item
6640  * 
6641  */
6642
6643
6644 /**
6645  * @class Roo.bootstrap.PaginationItem
6646  * @extends Roo.bootstrap.Component
6647  * Bootstrap PaginationItem class
6648  * @cfg {String} html text
6649  * @cfg {String} href the link
6650  * @cfg {Boolean} preventDefault (true | false) default true
6651  * @cfg {Boolean} active (true | false) default false
6652  * @cfg {Boolean} disabled default false
6653  * 
6654  * 
6655  * @constructor
6656  * Create a new PaginationItem
6657  * @param {Object} config The config object
6658  */
6659
6660
6661 Roo.bootstrap.PaginationItem = function(config){
6662     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6663     this.addEvents({
6664         // raw events
6665         /**
6666          * @event click
6667          * The raw click event for the entire grid.
6668          * @param {Roo.EventObject} e
6669          */
6670         "click" : true
6671     });
6672 };
6673
6674 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6675     
6676     href : false,
6677     html : false,
6678     preventDefault: true,
6679     active : false,
6680     cls : false,
6681     disabled: false,
6682     
6683     getAutoCreate : function(){
6684         var cfg= {
6685             tag: 'li',
6686             cn: [
6687                 {
6688                     tag : 'a',
6689                     href : this.href ? this.href : '#',
6690                     html : this.html ? this.html : ''
6691                 }
6692             ]
6693         };
6694         
6695         if(this.cls){
6696             cfg.cls = this.cls;
6697         }
6698         
6699         if(this.disabled){
6700             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6701         }
6702         
6703         if(this.active){
6704             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6705         }
6706         
6707         return cfg;
6708     },
6709     
6710     initEvents: function() {
6711         
6712         this.el.on('click', this.onClick, this);
6713         
6714     },
6715     onClick : function(e)
6716     {
6717         Roo.log('PaginationItem on click ');
6718         if(this.preventDefault){
6719             e.preventDefault();
6720         }
6721         
6722         if(this.disabled){
6723             return;
6724         }
6725         
6726         this.fireEvent('click', this, e);
6727     }
6728    
6729 });
6730
6731  
6732
6733  /*
6734  * - LGPL
6735  *
6736  * slider
6737  * 
6738  */
6739
6740
6741 /**
6742  * @class Roo.bootstrap.Slider
6743  * @extends Roo.bootstrap.Component
6744  * Bootstrap Slider class
6745  *    
6746  * @constructor
6747  * Create a new Slider
6748  * @param {Object} config The config object
6749  */
6750
6751 Roo.bootstrap.Slider = function(config){
6752     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6753 };
6754
6755 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6756     
6757     getAutoCreate : function(){
6758         
6759         var cfg = {
6760             tag: 'div',
6761             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6762             cn: [
6763                 {
6764                     tag: 'a',
6765                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6766                 }
6767             ]
6768         };
6769         
6770         return cfg;
6771     }
6772    
6773 });
6774
6775  /*
6776  * Based on:
6777  * Ext JS Library 1.1.1
6778  * Copyright(c) 2006-2007, Ext JS, LLC.
6779  *
6780  * Originally Released Under LGPL - original licence link has changed is not relivant.
6781  *
6782  * Fork - LGPL
6783  * <script type="text/javascript">
6784  */
6785  
6786
6787 /**
6788  * @class Roo.grid.ColumnModel
6789  * @extends Roo.util.Observable
6790  * This is the default implementation of a ColumnModel used by the Grid. It defines
6791  * the columns in the grid.
6792  * <br>Usage:<br>
6793  <pre><code>
6794  var colModel = new Roo.grid.ColumnModel([
6795         {header: "Ticker", width: 60, sortable: true, locked: true},
6796         {header: "Company Name", width: 150, sortable: true},
6797         {header: "Market Cap.", width: 100, sortable: true},
6798         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6799         {header: "Employees", width: 100, sortable: true, resizable: false}
6800  ]);
6801  </code></pre>
6802  * <p>
6803  
6804  * The config options listed for this class are options which may appear in each
6805  * individual column definition.
6806  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6807  * @constructor
6808  * @param {Object} config An Array of column config objects. See this class's
6809  * config objects for details.
6810 */
6811 Roo.grid.ColumnModel = function(config){
6812         /**
6813      * The config passed into the constructor
6814      */
6815     this.config = config;
6816     this.lookup = {};
6817
6818     // if no id, create one
6819     // if the column does not have a dataIndex mapping,
6820     // map it to the order it is in the config
6821     for(var i = 0, len = config.length; i < len; i++){
6822         var c = config[i];
6823         if(typeof c.dataIndex == "undefined"){
6824             c.dataIndex = i;
6825         }
6826         if(typeof c.renderer == "string"){
6827             c.renderer = Roo.util.Format[c.renderer];
6828         }
6829         if(typeof c.id == "undefined"){
6830             c.id = Roo.id();
6831         }
6832         if(c.editor && c.editor.xtype){
6833             c.editor  = Roo.factory(c.editor, Roo.grid);
6834         }
6835         if(c.editor && c.editor.isFormField){
6836             c.editor = new Roo.grid.GridEditor(c.editor);
6837         }
6838         this.lookup[c.id] = c;
6839     }
6840
6841     /**
6842      * The width of columns which have no width specified (defaults to 100)
6843      * @type Number
6844      */
6845     this.defaultWidth = 100;
6846
6847     /**
6848      * Default sortable of columns which have no sortable specified (defaults to false)
6849      * @type Boolean
6850      */
6851     this.defaultSortable = false;
6852
6853     this.addEvents({
6854         /**
6855              * @event widthchange
6856              * Fires when the width of a column changes.
6857              * @param {ColumnModel} this
6858              * @param {Number} columnIndex The column index
6859              * @param {Number} newWidth The new width
6860              */
6861             "widthchange": true,
6862         /**
6863              * @event headerchange
6864              * Fires when the text of a header changes.
6865              * @param {ColumnModel} this
6866              * @param {Number} columnIndex The column index
6867              * @param {Number} newText The new header text
6868              */
6869             "headerchange": true,
6870         /**
6871              * @event hiddenchange
6872              * Fires when a column is hidden or "unhidden".
6873              * @param {ColumnModel} this
6874              * @param {Number} columnIndex The column index
6875              * @param {Boolean} hidden true if hidden, false otherwise
6876              */
6877             "hiddenchange": true,
6878             /**
6879          * @event columnmoved
6880          * Fires when a column is moved.
6881          * @param {ColumnModel} this
6882          * @param {Number} oldIndex
6883          * @param {Number} newIndex
6884          */
6885         "columnmoved" : true,
6886         /**
6887          * @event columlockchange
6888          * Fires when a column's locked state is changed
6889          * @param {ColumnModel} this
6890          * @param {Number} colIndex
6891          * @param {Boolean} locked true if locked
6892          */
6893         "columnlockchange" : true
6894     });
6895     Roo.grid.ColumnModel.superclass.constructor.call(this);
6896 };
6897 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6898     /**
6899      * @cfg {String} header The header text to display in the Grid view.
6900      */
6901     /**
6902      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6903      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6904      * specified, the column's index is used as an index into the Record's data Array.
6905      */
6906     /**
6907      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6908      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6909      */
6910     /**
6911      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6912      * Defaults to the value of the {@link #defaultSortable} property.
6913      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6914      */
6915     /**
6916      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6917      */
6918     /**
6919      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6920      */
6921     /**
6922      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6923      */
6924     /**
6925      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6926      */
6927     /**
6928      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6929      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6930      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6931      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6932      */
6933        /**
6934      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6935      */
6936     /**
6937      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6938      */
6939     /**
6940      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6941      */
6942     /**
6943      * @cfg {String} cursor (Optional)
6944      */
6945     /**
6946      * @cfg {String} tooltip (Optional)
6947      */
6948     /**
6949      * @cfg {Number} xs (Optional)
6950      */
6951     /**
6952      * @cfg {Number} sm (Optional)
6953      */
6954     /**
6955      * @cfg {Number} md (Optional)
6956      */
6957     /**
6958      * @cfg {Number} lg (Optional)
6959      */
6960     /**
6961      * Returns the id of the column at the specified index.
6962      * @param {Number} index The column index
6963      * @return {String} the id
6964      */
6965     getColumnId : function(index){
6966         return this.config[index].id;
6967     },
6968
6969     /**
6970      * Returns the column for a specified id.
6971      * @param {String} id The column id
6972      * @return {Object} the column
6973      */
6974     getColumnById : function(id){
6975         return this.lookup[id];
6976     },
6977
6978     
6979     /**
6980      * Returns the column for a specified dataIndex.
6981      * @param {String} dataIndex The column dataIndex
6982      * @return {Object|Boolean} the column or false if not found
6983      */
6984     getColumnByDataIndex: function(dataIndex){
6985         var index = this.findColumnIndex(dataIndex);
6986         return index > -1 ? this.config[index] : false;
6987     },
6988     
6989     /**
6990      * Returns the index for a specified column id.
6991      * @param {String} id The column id
6992      * @return {Number} the index, or -1 if not found
6993      */
6994     getIndexById : function(id){
6995         for(var i = 0, len = this.config.length; i < len; i++){
6996             if(this.config[i].id == id){
6997                 return i;
6998             }
6999         }
7000         return -1;
7001     },
7002     
7003     /**
7004      * Returns the index for a specified column dataIndex.
7005      * @param {String} dataIndex The column dataIndex
7006      * @return {Number} the index, or -1 if not found
7007      */
7008     
7009     findColumnIndex : function(dataIndex){
7010         for(var i = 0, len = this.config.length; i < len; i++){
7011             if(this.config[i].dataIndex == dataIndex){
7012                 return i;
7013             }
7014         }
7015         return -1;
7016     },
7017     
7018     
7019     moveColumn : function(oldIndex, newIndex){
7020         var c = this.config[oldIndex];
7021         this.config.splice(oldIndex, 1);
7022         this.config.splice(newIndex, 0, c);
7023         this.dataMap = null;
7024         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7025     },
7026
7027     isLocked : function(colIndex){
7028         return this.config[colIndex].locked === true;
7029     },
7030
7031     setLocked : function(colIndex, value, suppressEvent){
7032         if(this.isLocked(colIndex) == value){
7033             return;
7034         }
7035         this.config[colIndex].locked = value;
7036         if(!suppressEvent){
7037             this.fireEvent("columnlockchange", this, colIndex, value);
7038         }
7039     },
7040
7041     getTotalLockedWidth : function(){
7042         var totalWidth = 0;
7043         for(var i = 0; i < this.config.length; i++){
7044             if(this.isLocked(i) && !this.isHidden(i)){
7045                 this.totalWidth += this.getColumnWidth(i);
7046             }
7047         }
7048         return totalWidth;
7049     },
7050
7051     getLockedCount : function(){
7052         for(var i = 0, len = this.config.length; i < len; i++){
7053             if(!this.isLocked(i)){
7054                 return i;
7055             }
7056         }
7057         
7058         return this.config.length;
7059     },
7060
7061     /**
7062      * Returns the number of columns.
7063      * @return {Number}
7064      */
7065     getColumnCount : function(visibleOnly){
7066         if(visibleOnly === true){
7067             var c = 0;
7068             for(var i = 0, len = this.config.length; i < len; i++){
7069                 if(!this.isHidden(i)){
7070                     c++;
7071                 }
7072             }
7073             return c;
7074         }
7075         return this.config.length;
7076     },
7077
7078     /**
7079      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7080      * @param {Function} fn
7081      * @param {Object} scope (optional)
7082      * @return {Array} result
7083      */
7084     getColumnsBy : function(fn, scope){
7085         var r = [];
7086         for(var i = 0, len = this.config.length; i < len; i++){
7087             var c = this.config[i];
7088             if(fn.call(scope||this, c, i) === true){
7089                 r[r.length] = c;
7090             }
7091         }
7092         return r;
7093     },
7094
7095     /**
7096      * Returns true if the specified column is sortable.
7097      * @param {Number} col The column index
7098      * @return {Boolean}
7099      */
7100     isSortable : function(col){
7101         if(typeof this.config[col].sortable == "undefined"){
7102             return this.defaultSortable;
7103         }
7104         return this.config[col].sortable;
7105     },
7106
7107     /**
7108      * Returns the rendering (formatting) function defined for the column.
7109      * @param {Number} col The column index.
7110      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7111      */
7112     getRenderer : function(col){
7113         if(!this.config[col].renderer){
7114             return Roo.grid.ColumnModel.defaultRenderer;
7115         }
7116         return this.config[col].renderer;
7117     },
7118
7119     /**
7120      * Sets the rendering (formatting) function for a column.
7121      * @param {Number} col The column index
7122      * @param {Function} fn The function to use to process the cell's raw data
7123      * to return HTML markup for the grid view. The render function is called with
7124      * the following parameters:<ul>
7125      * <li>Data value.</li>
7126      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7127      * <li>css A CSS style string to apply to the table cell.</li>
7128      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7129      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7130      * <li>Row index</li>
7131      * <li>Column index</li>
7132      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7133      */
7134     setRenderer : function(col, fn){
7135         this.config[col].renderer = fn;
7136     },
7137
7138     /**
7139      * Returns the width for the specified column.
7140      * @param {Number} col The column index
7141      * @return {Number}
7142      */
7143     getColumnWidth : function(col){
7144         return this.config[col].width * 1 || this.defaultWidth;
7145     },
7146
7147     /**
7148      * Sets the width for a column.
7149      * @param {Number} col The column index
7150      * @param {Number} width The new width
7151      */
7152     setColumnWidth : function(col, width, suppressEvent){
7153         this.config[col].width = width;
7154         this.totalWidth = null;
7155         if(!suppressEvent){
7156              this.fireEvent("widthchange", this, col, width);
7157         }
7158     },
7159
7160     /**
7161      * Returns the total width of all columns.
7162      * @param {Boolean} includeHidden True to include hidden column widths
7163      * @return {Number}
7164      */
7165     getTotalWidth : function(includeHidden){
7166         if(!this.totalWidth){
7167             this.totalWidth = 0;
7168             for(var i = 0, len = this.config.length; i < len; i++){
7169                 if(includeHidden || !this.isHidden(i)){
7170                     this.totalWidth += this.getColumnWidth(i);
7171                 }
7172             }
7173         }
7174         return this.totalWidth;
7175     },
7176
7177     /**
7178      * Returns the header for the specified column.
7179      * @param {Number} col The column index
7180      * @return {String}
7181      */
7182     getColumnHeader : function(col){
7183         return this.config[col].header;
7184     },
7185
7186     /**
7187      * Sets the header for a column.
7188      * @param {Number} col The column index
7189      * @param {String} header The new header
7190      */
7191     setColumnHeader : function(col, header){
7192         this.config[col].header = header;
7193         this.fireEvent("headerchange", this, col, header);
7194     },
7195
7196     /**
7197      * Returns the tooltip for the specified column.
7198      * @param {Number} col The column index
7199      * @return {String}
7200      */
7201     getColumnTooltip : function(col){
7202             return this.config[col].tooltip;
7203     },
7204     /**
7205      * Sets the tooltip for a column.
7206      * @param {Number} col The column index
7207      * @param {String} tooltip The new tooltip
7208      */
7209     setColumnTooltip : function(col, tooltip){
7210             this.config[col].tooltip = tooltip;
7211     },
7212
7213     /**
7214      * Returns the dataIndex for the specified column.
7215      * @param {Number} col The column index
7216      * @return {Number}
7217      */
7218     getDataIndex : function(col){
7219         return this.config[col].dataIndex;
7220     },
7221
7222     /**
7223      * Sets the dataIndex for a column.
7224      * @param {Number} col The column index
7225      * @param {Number} dataIndex The new dataIndex
7226      */
7227     setDataIndex : function(col, dataIndex){
7228         this.config[col].dataIndex = dataIndex;
7229     },
7230
7231     
7232     
7233     /**
7234      * Returns true if the cell is editable.
7235      * @param {Number} colIndex The column index
7236      * @param {Number} rowIndex The row index - this is nto actually used..?
7237      * @return {Boolean}
7238      */
7239     isCellEditable : function(colIndex, rowIndex){
7240         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7241     },
7242
7243     /**
7244      * Returns the editor defined for the cell/column.
7245      * return false or null to disable editing.
7246      * @param {Number} colIndex The column index
7247      * @param {Number} rowIndex The row index
7248      * @return {Object}
7249      */
7250     getCellEditor : function(colIndex, rowIndex){
7251         return this.config[colIndex].editor;
7252     },
7253
7254     /**
7255      * Sets if a column is editable.
7256      * @param {Number} col The column index
7257      * @param {Boolean} editable True if the column is editable
7258      */
7259     setEditable : function(col, editable){
7260         this.config[col].editable = editable;
7261     },
7262
7263
7264     /**
7265      * Returns true if the column is hidden.
7266      * @param {Number} colIndex The column index
7267      * @return {Boolean}
7268      */
7269     isHidden : function(colIndex){
7270         return this.config[colIndex].hidden;
7271     },
7272
7273
7274     /**
7275      * Returns true if the column width cannot be changed
7276      */
7277     isFixed : function(colIndex){
7278         return this.config[colIndex].fixed;
7279     },
7280
7281     /**
7282      * Returns true if the column can be resized
7283      * @return {Boolean}
7284      */
7285     isResizable : function(colIndex){
7286         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7287     },
7288     /**
7289      * Sets if a column is hidden.
7290      * @param {Number} colIndex The column index
7291      * @param {Boolean} hidden True if the column is hidden
7292      */
7293     setHidden : function(colIndex, hidden){
7294         this.config[colIndex].hidden = hidden;
7295         this.totalWidth = null;
7296         this.fireEvent("hiddenchange", this, colIndex, hidden);
7297     },
7298
7299     /**
7300      * Sets the editor for a column.
7301      * @param {Number} col The column index
7302      * @param {Object} editor The editor object
7303      */
7304     setEditor : function(col, editor){
7305         this.config[col].editor = editor;
7306     }
7307 });
7308
7309 Roo.grid.ColumnModel.defaultRenderer = function(value)
7310 {
7311     if(typeof value == "object") {
7312         return value;
7313     }
7314         if(typeof value == "string" && value.length < 1){
7315             return "&#160;";
7316         }
7317     
7318         return String.format("{0}", value);
7319 };
7320
7321 // Alias for backwards compatibility
7322 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7323 /*
7324  * Based on:
7325  * Ext JS Library 1.1.1
7326  * Copyright(c) 2006-2007, Ext JS, LLC.
7327  *
7328  * Originally Released Under LGPL - original licence link has changed is not relivant.
7329  *
7330  * Fork - LGPL
7331  * <script type="text/javascript">
7332  */
7333  
7334 /**
7335  * @class Roo.LoadMask
7336  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7337  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7338  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7339  * element's UpdateManager load indicator and will be destroyed after the initial load.
7340  * @constructor
7341  * Create a new LoadMask
7342  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7343  * @param {Object} config The config object
7344  */
7345 Roo.LoadMask = function(el, config){
7346     this.el = Roo.get(el);
7347     Roo.apply(this, config);
7348     if(this.store){
7349         this.store.on('beforeload', this.onBeforeLoad, this);
7350         this.store.on('load', this.onLoad, this);
7351         this.store.on('loadexception', this.onLoadException, this);
7352         this.removeMask = false;
7353     }else{
7354         var um = this.el.getUpdateManager();
7355         um.showLoadIndicator = false; // disable the default indicator
7356         um.on('beforeupdate', this.onBeforeLoad, this);
7357         um.on('update', this.onLoad, this);
7358         um.on('failure', this.onLoad, this);
7359         this.removeMask = true;
7360     }
7361 };
7362
7363 Roo.LoadMask.prototype = {
7364     /**
7365      * @cfg {Boolean} removeMask
7366      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7367      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7368      */
7369     /**
7370      * @cfg {String} msg
7371      * The text to display in a centered loading message box (defaults to 'Loading...')
7372      */
7373     msg : 'Loading...',
7374     /**
7375      * @cfg {String} msgCls
7376      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7377      */
7378     msgCls : 'x-mask-loading',
7379
7380     /**
7381      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7382      * @type Boolean
7383      */
7384     disabled: false,
7385
7386     /**
7387      * Disables the mask to prevent it from being displayed
7388      */
7389     disable : function(){
7390        this.disabled = true;
7391     },
7392
7393     /**
7394      * Enables the mask so that it can be displayed
7395      */
7396     enable : function(){
7397         this.disabled = false;
7398     },
7399     
7400     onLoadException : function()
7401     {
7402         Roo.log(arguments);
7403         
7404         if (typeof(arguments[3]) != 'undefined') {
7405             Roo.MessageBox.alert("Error loading",arguments[3]);
7406         } 
7407         /*
7408         try {
7409             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7410                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7411             }   
7412         } catch(e) {
7413             
7414         }
7415         */
7416     
7417         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7418     },
7419     // private
7420     onLoad : function()
7421     {
7422         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7423     },
7424
7425     // private
7426     onBeforeLoad : function(){
7427         if(!this.disabled){
7428             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7429         }
7430     },
7431
7432     // private
7433     destroy : function(){
7434         if(this.store){
7435             this.store.un('beforeload', this.onBeforeLoad, this);
7436             this.store.un('load', this.onLoad, this);
7437             this.store.un('loadexception', this.onLoadException, this);
7438         }else{
7439             var um = this.el.getUpdateManager();
7440             um.un('beforeupdate', this.onBeforeLoad, this);
7441             um.un('update', this.onLoad, this);
7442             um.un('failure', this.onLoad, this);
7443         }
7444     }
7445 };/*
7446  * - LGPL
7447  *
7448  * table
7449  * 
7450  */
7451
7452 /**
7453  * @class Roo.bootstrap.Table
7454  * @extends Roo.bootstrap.Component
7455  * Bootstrap Table class
7456  * @cfg {String} cls table class
7457  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7458  * @cfg {String} bgcolor Specifies the background color for a table
7459  * @cfg {Number} border Specifies whether the table cells should have borders or not
7460  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7461  * @cfg {Number} cellspacing Specifies the space between cells
7462  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7463  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7464  * @cfg {String} sortable Specifies that the table should be sortable
7465  * @cfg {String} summary Specifies a summary of the content of a table
7466  * @cfg {Number} width Specifies the width of a table
7467  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7468  * 
7469  * @cfg {boolean} striped Should the rows be alternative striped
7470  * @cfg {boolean} bordered Add borders to the table
7471  * @cfg {boolean} hover Add hover highlighting
7472  * @cfg {boolean} condensed Format condensed
7473  * @cfg {boolean} responsive Format condensed
7474  * @cfg {Boolean} loadMask (true|false) default false
7475  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7476  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7477  * @cfg {Boolean} rowSelection (true|false) default false
7478  * @cfg {Boolean} cellSelection (true|false) default false
7479  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7480  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7481  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7482  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7483  
7484  * 
7485  * @constructor
7486  * Create a new Table
7487  * @param {Object} config The config object
7488  */
7489
7490 Roo.bootstrap.Table = function(config){
7491     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7492     
7493   
7494     
7495     // BC...
7496     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7497     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7498     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7499     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7500     
7501     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7502     if (this.sm) {
7503         this.sm.grid = this;
7504         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7505         this.sm = this.selModel;
7506         this.sm.xmodule = this.xmodule || false;
7507     }
7508     
7509     if (this.cm && typeof(this.cm.config) == 'undefined') {
7510         this.colModel = new Roo.grid.ColumnModel(this.cm);
7511         this.cm = this.colModel;
7512         this.cm.xmodule = this.xmodule || false;
7513     }
7514     if (this.store) {
7515         this.store= Roo.factory(this.store, Roo.data);
7516         this.ds = this.store;
7517         this.ds.xmodule = this.xmodule || false;
7518          
7519     }
7520     if (this.footer && this.store) {
7521         this.footer.dataSource = this.ds;
7522         this.footer = Roo.factory(this.footer);
7523     }
7524     
7525     /** @private */
7526     this.addEvents({
7527         /**
7528          * @event cellclick
7529          * Fires when a cell is clicked
7530          * @param {Roo.bootstrap.Table} this
7531          * @param {Roo.Element} el
7532          * @param {Number} rowIndex
7533          * @param {Number} columnIndex
7534          * @param {Roo.EventObject} e
7535          */
7536         "cellclick" : true,
7537         /**
7538          * @event celldblclick
7539          * Fires when a cell is double clicked
7540          * @param {Roo.bootstrap.Table} this
7541          * @param {Roo.Element} el
7542          * @param {Number} rowIndex
7543          * @param {Number} columnIndex
7544          * @param {Roo.EventObject} e
7545          */
7546         "celldblclick" : true,
7547         /**
7548          * @event rowclick
7549          * Fires when a row is clicked
7550          * @param {Roo.bootstrap.Table} this
7551          * @param {Roo.Element} el
7552          * @param {Number} rowIndex
7553          * @param {Roo.EventObject} e
7554          */
7555         "rowclick" : true,
7556         /**
7557          * @event rowdblclick
7558          * Fires when a row is double clicked
7559          * @param {Roo.bootstrap.Table} this
7560          * @param {Roo.Element} el
7561          * @param {Number} rowIndex
7562          * @param {Roo.EventObject} e
7563          */
7564         "rowdblclick" : true,
7565         /**
7566          * @event mouseover
7567          * Fires when a mouseover occur
7568          * @param {Roo.bootstrap.Table} this
7569          * @param {Roo.Element} el
7570          * @param {Number} rowIndex
7571          * @param {Number} columnIndex
7572          * @param {Roo.EventObject} e
7573          */
7574         "mouseover" : true,
7575         /**
7576          * @event mouseout
7577          * Fires when a mouseout occur
7578          * @param {Roo.bootstrap.Table} this
7579          * @param {Roo.Element} el
7580          * @param {Number} rowIndex
7581          * @param {Number} columnIndex
7582          * @param {Roo.EventObject} e
7583          */
7584         "mouseout" : true,
7585         /**
7586          * @event rowclass
7587          * Fires when a row is rendered, so you can change add a style to it.
7588          * @param {Roo.bootstrap.Table} this
7589          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7590          */
7591         'rowclass' : true,
7592           /**
7593          * @event rowsrendered
7594          * Fires when all the  rows have been rendered
7595          * @param {Roo.bootstrap.Table} this
7596          */
7597         'rowsrendered' : true,
7598         /**
7599          * @event contextmenu
7600          * The raw contextmenu event for the entire grid.
7601          * @param {Roo.EventObject} e
7602          */
7603         "contextmenu" : true,
7604         /**
7605          * @event rowcontextmenu
7606          * Fires when a row is right clicked
7607          * @param {Roo.bootstrap.Table} this
7608          * @param {Number} rowIndex
7609          * @param {Roo.EventObject} e
7610          */
7611         "rowcontextmenu" : true,
7612         /**
7613          * @event cellcontextmenu
7614          * Fires when a cell is right clicked
7615          * @param {Roo.bootstrap.Table} this
7616          * @param {Number} rowIndex
7617          * @param {Number} cellIndex
7618          * @param {Roo.EventObject} e
7619          */
7620          "cellcontextmenu" : true,
7621          /**
7622          * @event headercontextmenu
7623          * Fires when a header is right clicked
7624          * @param {Roo.bootstrap.Table} this
7625          * @param {Number} columnIndex
7626          * @param {Roo.EventObject} e
7627          */
7628         "headercontextmenu" : true
7629     });
7630 };
7631
7632 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7633     
7634     cls: false,
7635     align: false,
7636     bgcolor: false,
7637     border: false,
7638     cellpadding: false,
7639     cellspacing: false,
7640     frame: false,
7641     rules: false,
7642     sortable: false,
7643     summary: false,
7644     width: false,
7645     striped : false,
7646     scrollBody : false,
7647     bordered: false,
7648     hover:  false,
7649     condensed : false,
7650     responsive : false,
7651     sm : false,
7652     cm : false,
7653     store : false,
7654     loadMask : false,
7655     footerShow : true,
7656     headerShow : true,
7657   
7658     rowSelection : false,
7659     cellSelection : false,
7660     layout : false,
7661     
7662     // Roo.Element - the tbody
7663     mainBody: false,
7664     // Roo.Element - thead element
7665     mainHead: false,
7666     
7667     container: false, // used by gridpanel...
7668     
7669     lazyLoad : false,
7670     
7671     CSS : Roo.util.CSS,
7672     
7673     auto_hide_footer : false,
7674     
7675     getAutoCreate : function()
7676     {
7677         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7678         
7679         cfg = {
7680             tag: 'table',
7681             cls : 'table',
7682             cn : []
7683         };
7684         if (this.scrollBody) {
7685             cfg.cls += ' table-body-fixed';
7686         }    
7687         if (this.striped) {
7688             cfg.cls += ' table-striped';
7689         }
7690         
7691         if (this.hover) {
7692             cfg.cls += ' table-hover';
7693         }
7694         if (this.bordered) {
7695             cfg.cls += ' table-bordered';
7696         }
7697         if (this.condensed) {
7698             cfg.cls += ' table-condensed';
7699         }
7700         if (this.responsive) {
7701             cfg.cls += ' table-responsive';
7702         }
7703         
7704         if (this.cls) {
7705             cfg.cls+=  ' ' +this.cls;
7706         }
7707         
7708         // this lot should be simplifed...
7709         var _t = this;
7710         var cp = [
7711             'align',
7712             'bgcolor',
7713             'border',
7714             'cellpadding',
7715             'cellspacing',
7716             'frame',
7717             'rules',
7718             'sortable',
7719             'summary',
7720             'width'
7721         ].forEach(function(k) {
7722             if (_t[k]) {
7723                 cfg[k] = _t[k];
7724             }
7725         });
7726         
7727         
7728         if (this.layout) {
7729             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7730         }
7731         
7732         if(this.store || this.cm){
7733             if(this.headerShow){
7734                 cfg.cn.push(this.renderHeader());
7735             }
7736             
7737             cfg.cn.push(this.renderBody());
7738             
7739             if(this.footerShow){
7740                 cfg.cn.push(this.renderFooter());
7741             }
7742             // where does this come from?
7743             //cfg.cls+=  ' TableGrid';
7744         }
7745         
7746         return { cn : [ cfg ] };
7747     },
7748     
7749     initEvents : function()
7750     {   
7751         if(!this.store || !this.cm){
7752             return;
7753         }
7754         if (this.selModel) {
7755             this.selModel.initEvents();
7756         }
7757         
7758         
7759         //Roo.log('initEvents with ds!!!!');
7760         
7761         this.mainBody = this.el.select('tbody', true).first();
7762         this.mainHead = this.el.select('thead', true).first();
7763         this.mainFoot = this.el.select('tfoot', true).first();
7764         
7765         
7766         
7767         var _this = this;
7768         
7769         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7770             e.on('click', _this.sort, _this);
7771         });
7772         
7773         this.mainBody.on("click", this.onClick, this);
7774         this.mainBody.on("dblclick", this.onDblClick, this);
7775         
7776         // why is this done????? = it breaks dialogs??
7777         //this.parent().el.setStyle('position', 'relative');
7778         
7779         
7780         if (this.footer) {
7781             this.footer.parentId = this.id;
7782             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7783             
7784             if(this.lazyLoad){
7785                 this.el.select('tfoot tr td').first().addClass('hide');
7786             }
7787         } 
7788         
7789         if(this.loadMask) {
7790             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7791         }
7792         
7793         this.store.on('load', this.onLoad, this);
7794         this.store.on('beforeload', this.onBeforeLoad, this);
7795         this.store.on('update', this.onUpdate, this);
7796         this.store.on('add', this.onAdd, this);
7797         this.store.on("clear", this.clear, this);
7798         
7799         this.el.on("contextmenu", this.onContextMenu, this);
7800         
7801         this.mainBody.on('scroll', this.onBodyScroll, this);
7802         
7803         this.cm.on("headerchange", this.onHeaderChange, this);
7804         
7805         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7806         
7807     },
7808     
7809     onContextMenu : function(e, t)
7810     {
7811         this.processEvent("contextmenu", e);
7812     },
7813     
7814     processEvent : function(name, e)
7815     {
7816         if (name != 'touchstart' ) {
7817             this.fireEvent(name, e);    
7818         }
7819         
7820         var t = e.getTarget();
7821         
7822         var cell = Roo.get(t);
7823         
7824         if(!cell){
7825             return;
7826         }
7827         
7828         if(cell.findParent('tfoot', false, true)){
7829             return;
7830         }
7831         
7832         if(cell.findParent('thead', false, true)){
7833             
7834             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7835                 cell = Roo.get(t).findParent('th', false, true);
7836                 if (!cell) {
7837                     Roo.log("failed to find th in thead?");
7838                     Roo.log(e.getTarget());
7839                     return;
7840                 }
7841             }
7842             
7843             var cellIndex = cell.dom.cellIndex;
7844             
7845             var ename = name == 'touchstart' ? 'click' : name;
7846             this.fireEvent("header" + ename, this, cellIndex, e);
7847             
7848             return;
7849         }
7850         
7851         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7852             cell = Roo.get(t).findParent('td', false, true);
7853             if (!cell) {
7854                 Roo.log("failed to find th in tbody?");
7855                 Roo.log(e.getTarget());
7856                 return;
7857             }
7858         }
7859         
7860         var row = cell.findParent('tr', false, true);
7861         var cellIndex = cell.dom.cellIndex;
7862         var rowIndex = row.dom.rowIndex - 1;
7863         
7864         if(row !== false){
7865             
7866             this.fireEvent("row" + name, this, rowIndex, e);
7867             
7868             if(cell !== false){
7869             
7870                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7871             }
7872         }
7873         
7874     },
7875     
7876     onMouseover : function(e, el)
7877     {
7878         var cell = Roo.get(el);
7879         
7880         if(!cell){
7881             return;
7882         }
7883         
7884         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7885             cell = cell.findParent('td', false, true);
7886         }
7887         
7888         var row = cell.findParent('tr', false, true);
7889         var cellIndex = cell.dom.cellIndex;
7890         var rowIndex = row.dom.rowIndex - 1; // start from 0
7891         
7892         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7893         
7894     },
7895     
7896     onMouseout : function(e, el)
7897     {
7898         var cell = Roo.get(el);
7899         
7900         if(!cell){
7901             return;
7902         }
7903         
7904         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7905             cell = cell.findParent('td', false, true);
7906         }
7907         
7908         var row = cell.findParent('tr', false, true);
7909         var cellIndex = cell.dom.cellIndex;
7910         var rowIndex = row.dom.rowIndex - 1; // start from 0
7911         
7912         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7913         
7914     },
7915     
7916     onClick : function(e, el)
7917     {
7918         var cell = Roo.get(el);
7919         
7920         if(!cell || (!this.cellSelection && !this.rowSelection)){
7921             return;
7922         }
7923         
7924         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7925             cell = cell.findParent('td', false, true);
7926         }
7927         
7928         if(!cell || typeof(cell) == 'undefined'){
7929             return;
7930         }
7931         
7932         var row = cell.findParent('tr', false, true);
7933         
7934         if(!row || typeof(row) == 'undefined'){
7935             return;
7936         }
7937         
7938         var cellIndex = cell.dom.cellIndex;
7939         var rowIndex = this.getRowIndex(row);
7940         
7941         // why??? - should these not be based on SelectionModel?
7942         if(this.cellSelection){
7943             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7944         }
7945         
7946         if(this.rowSelection){
7947             this.fireEvent('rowclick', this, row, rowIndex, e);
7948         }
7949         
7950         
7951     },
7952         
7953     onDblClick : function(e,el)
7954     {
7955         var cell = Roo.get(el);
7956         
7957         if(!cell || (!this.cellSelection && !this.rowSelection)){
7958             return;
7959         }
7960         
7961         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7962             cell = cell.findParent('td', false, true);
7963         }
7964         
7965         if(!cell || typeof(cell) == 'undefined'){
7966             return;
7967         }
7968         
7969         var row = cell.findParent('tr', false, true);
7970         
7971         if(!row || typeof(row) == 'undefined'){
7972             return;
7973         }
7974         
7975         var cellIndex = cell.dom.cellIndex;
7976         var rowIndex = this.getRowIndex(row);
7977         
7978         if(this.cellSelection){
7979             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
7980         }
7981         
7982         if(this.rowSelection){
7983             this.fireEvent('rowdblclick', this, row, rowIndex, e);
7984         }
7985     },
7986     
7987     sort : function(e,el)
7988     {
7989         var col = Roo.get(el);
7990         
7991         if(!col.hasClass('sortable')){
7992             return;
7993         }
7994         
7995         var sort = col.attr('sort');
7996         var dir = 'ASC';
7997         
7998         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
7999             dir = 'DESC';
8000         }
8001         
8002         this.store.sortInfo = {field : sort, direction : dir};
8003         
8004         if (this.footer) {
8005             Roo.log("calling footer first");
8006             this.footer.onClick('first');
8007         } else {
8008         
8009             this.store.load({ params : { start : 0 } });
8010         }
8011     },
8012     
8013     renderHeader : function()
8014     {
8015         var header = {
8016             tag: 'thead',
8017             cn : []
8018         };
8019         
8020         var cm = this.cm;
8021         this.totalWidth = 0;
8022         
8023         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8024             
8025             var config = cm.config[i];
8026             
8027             var c = {
8028                 tag: 'th',
8029                 cls : 'x-hcol-' + i,
8030                 style : '',
8031                 html: cm.getColumnHeader(i)
8032             };
8033             
8034             var hh = '';
8035             
8036             if(typeof(config.sortable) != 'undefined' && config.sortable){
8037                 c.cls = 'sortable';
8038                 c.html = '<i class="glyphicon"></i>' + c.html;
8039             }
8040             
8041             // could use BS4 hidden-..-down 
8042             
8043             if(typeof(config.lgHeader) != 'undefined'){
8044                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8045             }
8046             
8047             if(typeof(config.mdHeader) != 'undefined'){
8048                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8049             }
8050             
8051             if(typeof(config.smHeader) != 'undefined'){
8052                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8053             }
8054             
8055             if(typeof(config.xsHeader) != 'undefined'){
8056                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8057             }
8058             
8059             if(hh.length){
8060                 c.html = hh;
8061             }
8062             
8063             if(typeof(config.tooltip) != 'undefined'){
8064                 c.tooltip = config.tooltip;
8065             }
8066             
8067             if(typeof(config.colspan) != 'undefined'){
8068                 c.colspan = config.colspan;
8069             }
8070             
8071             if(typeof(config.hidden) != 'undefined' && config.hidden){
8072                 c.style += ' display:none;';
8073             }
8074             
8075             if(typeof(config.dataIndex) != 'undefined'){
8076                 c.sort = config.dataIndex;
8077             }
8078             
8079            
8080             
8081             if(typeof(config.align) != 'undefined' && config.align.length){
8082                 c.style += ' text-align:' + config.align + ';';
8083             }
8084             
8085             if(typeof(config.width) != 'undefined'){
8086                 c.style += ' width:' + config.width + 'px;';
8087                 this.totalWidth += config.width;
8088             } else {
8089                 this.totalWidth += 100; // assume minimum of 100 per column?
8090             }
8091             
8092             if(typeof(config.cls) != 'undefined'){
8093                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8094             }
8095             
8096             ['xs','sm','md','lg'].map(function(size){
8097                 
8098                 if(typeof(config[size]) == 'undefined'){
8099                     return;
8100                 }
8101                  
8102                 if (!config[size]) { // 0 = hidden
8103                     // BS 4 '0' is treated as hide that column and below.
8104                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8105                     return;
8106                 }
8107                 
8108                 c.cls += ' col-' + size + '-' + config[size] + (
8109                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8110                 );
8111                 
8112                 
8113             });
8114             
8115             header.cn.push(c)
8116         }
8117         
8118         return header;
8119     },
8120     
8121     renderBody : function()
8122     {
8123         var body = {
8124             tag: 'tbody',
8125             cn : [
8126                 {
8127                     tag: 'tr',
8128                     cn : [
8129                         {
8130                             tag : 'td',
8131                             colspan :  this.cm.getColumnCount()
8132                         }
8133                     ]
8134                 }
8135             ]
8136         };
8137         
8138         return body;
8139     },
8140     
8141     renderFooter : function()
8142     {
8143         var footer = {
8144             tag: 'tfoot',
8145             cn : [
8146                 {
8147                     tag: 'tr',
8148                     cn : [
8149                         {
8150                             tag : 'td',
8151                             colspan :  this.cm.getColumnCount()
8152                         }
8153                     ]
8154                 }
8155             ]
8156         };
8157         
8158         return footer;
8159     },
8160     
8161     
8162     
8163     onLoad : function()
8164     {
8165 //        Roo.log('ds onload');
8166         this.clear();
8167         
8168         var _this = this;
8169         var cm = this.cm;
8170         var ds = this.store;
8171         
8172         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8173             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8174             if (_this.store.sortInfo) {
8175                     
8176                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8177                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8178                 }
8179                 
8180                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8181                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8182                 }
8183             }
8184         });
8185         
8186         var tbody =  this.mainBody;
8187               
8188         if(ds.getCount() > 0){
8189             ds.data.each(function(d,rowIndex){
8190                 var row =  this.renderRow(cm, ds, rowIndex);
8191                 
8192                 tbody.createChild(row);
8193                 
8194                 var _this = this;
8195                 
8196                 if(row.cellObjects.length){
8197                     Roo.each(row.cellObjects, function(r){
8198                         _this.renderCellObject(r);
8199                     })
8200                 }
8201                 
8202             }, this);
8203         }
8204         
8205         var tfoot = this.el.select('tfoot', true).first();
8206         
8207         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8208             
8209             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8210             
8211             var total = this.ds.getTotalCount();
8212             
8213             if(this.footer.pageSize < total){
8214                 this.mainFoot.show();
8215             }
8216         }
8217         
8218         Roo.each(this.el.select('tbody td', true).elements, function(e){
8219             e.on('mouseover', _this.onMouseover, _this);
8220         });
8221         
8222         Roo.each(this.el.select('tbody td', true).elements, function(e){
8223             e.on('mouseout', _this.onMouseout, _this);
8224         });
8225         this.fireEvent('rowsrendered', this);
8226         
8227         this.autoSize();
8228     },
8229     
8230     
8231     onUpdate : function(ds,record)
8232     {
8233         this.refreshRow(record);
8234         this.autoSize();
8235     },
8236     
8237     onRemove : function(ds, record, index, isUpdate){
8238         if(isUpdate !== true){
8239             this.fireEvent("beforerowremoved", this, index, record);
8240         }
8241         var bt = this.mainBody.dom;
8242         
8243         var rows = this.el.select('tbody > tr', true).elements;
8244         
8245         if(typeof(rows[index]) != 'undefined'){
8246             bt.removeChild(rows[index].dom);
8247         }
8248         
8249 //        if(bt.rows[index]){
8250 //            bt.removeChild(bt.rows[index]);
8251 //        }
8252         
8253         if(isUpdate !== true){
8254             //this.stripeRows(index);
8255             //this.syncRowHeights(index, index);
8256             //this.layout();
8257             this.fireEvent("rowremoved", this, index, record);
8258         }
8259     },
8260     
8261     onAdd : function(ds, records, rowIndex)
8262     {
8263         //Roo.log('on Add called');
8264         // - note this does not handle multiple adding very well..
8265         var bt = this.mainBody.dom;
8266         for (var i =0 ; i < records.length;i++) {
8267             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8268             //Roo.log(records[i]);
8269             //Roo.log(this.store.getAt(rowIndex+i));
8270             this.insertRow(this.store, rowIndex + i, false);
8271             return;
8272         }
8273         
8274     },
8275     
8276     
8277     refreshRow : function(record){
8278         var ds = this.store, index;
8279         if(typeof record == 'number'){
8280             index = record;
8281             record = ds.getAt(index);
8282         }else{
8283             index = ds.indexOf(record);
8284             if (index < 0) {
8285                 return; // should not happen - but seems to 
8286             }
8287         }
8288         this.insertRow(ds, index, true);
8289         this.autoSize();
8290         this.onRemove(ds, record, index+1, true);
8291         this.autoSize();
8292         //this.syncRowHeights(index, index);
8293         //this.layout();
8294         this.fireEvent("rowupdated", this, index, record);
8295     },
8296     
8297     insertRow : function(dm, rowIndex, isUpdate){
8298         
8299         if(!isUpdate){
8300             this.fireEvent("beforerowsinserted", this, rowIndex);
8301         }
8302             //var s = this.getScrollState();
8303         var row = this.renderRow(this.cm, this.store, rowIndex);
8304         // insert before rowIndex..
8305         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8306         
8307         var _this = this;
8308                 
8309         if(row.cellObjects.length){
8310             Roo.each(row.cellObjects, function(r){
8311                 _this.renderCellObject(r);
8312             })
8313         }
8314             
8315         if(!isUpdate){
8316             this.fireEvent("rowsinserted", this, rowIndex);
8317             //this.syncRowHeights(firstRow, lastRow);
8318             //this.stripeRows(firstRow);
8319             //this.layout();
8320         }
8321         
8322     },
8323     
8324     
8325     getRowDom : function(rowIndex)
8326     {
8327         var rows = this.el.select('tbody > tr', true).elements;
8328         
8329         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8330         
8331     },
8332     // returns the object tree for a tr..
8333   
8334     
8335     renderRow : function(cm, ds, rowIndex) 
8336     {
8337         var d = ds.getAt(rowIndex);
8338         
8339         var row = {
8340             tag : 'tr',
8341             cls : 'x-row-' + rowIndex,
8342             cn : []
8343         };
8344             
8345         var cellObjects = [];
8346         
8347         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8348             var config = cm.config[i];
8349             
8350             var renderer = cm.getRenderer(i);
8351             var value = '';
8352             var id = false;
8353             
8354             if(typeof(renderer) !== 'undefined'){
8355                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8356             }
8357             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8358             // and are rendered into the cells after the row is rendered - using the id for the element.
8359             
8360             if(typeof(value) === 'object'){
8361                 id = Roo.id();
8362                 cellObjects.push({
8363                     container : id,
8364                     cfg : value 
8365                 })
8366             }
8367             
8368             var rowcfg = {
8369                 record: d,
8370                 rowIndex : rowIndex,
8371                 colIndex : i,
8372                 rowClass : ''
8373             };
8374
8375             this.fireEvent('rowclass', this, rowcfg);
8376             
8377             var td = {
8378                 tag: 'td',
8379                 cls : rowcfg.rowClass + ' x-col-' + i,
8380                 style: '',
8381                 html: (typeof(value) === 'object') ? '' : value
8382             };
8383             
8384             if (id) {
8385                 td.id = id;
8386             }
8387             
8388             if(typeof(config.colspan) != 'undefined'){
8389                 td.colspan = config.colspan;
8390             }
8391             
8392             if(typeof(config.hidden) != 'undefined' && config.hidden){
8393                 td.style += ' display:none;';
8394             }
8395             
8396             if(typeof(config.align) != 'undefined' && config.align.length){
8397                 td.style += ' text-align:' + config.align + ';';
8398             }
8399             if(typeof(config.valign) != 'undefined' && config.valign.length){
8400                 td.style += ' vertical-align:' + config.valign + ';';
8401             }
8402             
8403             if(typeof(config.width) != 'undefined'){
8404                 td.style += ' width:' +  config.width + 'px;';
8405             }
8406             
8407             if(typeof(config.cursor) != 'undefined'){
8408                 td.style += ' cursor:' +  config.cursor + ';';
8409             }
8410             
8411             if(typeof(config.cls) != 'undefined'){
8412                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8413             }
8414             
8415             ['xs','sm','md','lg'].map(function(size){
8416                 
8417                 if(typeof(config[size]) == 'undefined'){
8418                     return;
8419                 }
8420                 
8421                 
8422                   
8423                 if (!config[size]) { // 0 = hidden
8424                     // BS 4 '0' is treated as hide that column and below.
8425                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8426                     return;
8427                 }
8428                 
8429                 td.cls += ' col-' + size + '-' + config[size] + (
8430                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8431                 );
8432                  
8433
8434             });
8435             
8436             row.cn.push(td);
8437            
8438         }
8439         
8440         row.cellObjects = cellObjects;
8441         
8442         return row;
8443           
8444     },
8445     
8446     
8447     
8448     onBeforeLoad : function()
8449     {
8450         
8451     },
8452      /**
8453      * Remove all rows
8454      */
8455     clear : function()
8456     {
8457         this.el.select('tbody', true).first().dom.innerHTML = '';
8458     },
8459     /**
8460      * Show or hide a row.
8461      * @param {Number} rowIndex to show or hide
8462      * @param {Boolean} state hide
8463      */
8464     setRowVisibility : function(rowIndex, state)
8465     {
8466         var bt = this.mainBody.dom;
8467         
8468         var rows = this.el.select('tbody > tr', true).elements;
8469         
8470         if(typeof(rows[rowIndex]) == 'undefined'){
8471             return;
8472         }
8473         rows[rowIndex].dom.style.display = state ? '' : 'none';
8474     },
8475     
8476     
8477     getSelectionModel : function(){
8478         if(!this.selModel){
8479             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8480         }
8481         return this.selModel;
8482     },
8483     /*
8484      * Render the Roo.bootstrap object from renderder
8485      */
8486     renderCellObject : function(r)
8487     {
8488         var _this = this;
8489         
8490         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8491         
8492         var t = r.cfg.render(r.container);
8493         
8494         if(r.cfg.cn){
8495             Roo.each(r.cfg.cn, function(c){
8496                 var child = {
8497                     container: t.getChildContainer(),
8498                     cfg: c
8499                 };
8500                 _this.renderCellObject(child);
8501             })
8502         }
8503     },
8504     
8505     getRowIndex : function(row)
8506     {
8507         var rowIndex = -1;
8508         
8509         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8510             if(el != row){
8511                 return;
8512             }
8513             
8514             rowIndex = index;
8515         });
8516         
8517         return rowIndex;
8518     },
8519      /**
8520      * Returns the grid's underlying element = used by panel.Grid
8521      * @return {Element} The element
8522      */
8523     getGridEl : function(){
8524         return this.el;
8525     },
8526      /**
8527      * Forces a resize - used by panel.Grid
8528      * @return {Element} The element
8529      */
8530     autoSize : function()
8531     {
8532         //var ctr = Roo.get(this.container.dom.parentElement);
8533         var ctr = Roo.get(this.el.dom);
8534         
8535         var thd = this.getGridEl().select('thead',true).first();
8536         var tbd = this.getGridEl().select('tbody', true).first();
8537         var tfd = this.getGridEl().select('tfoot', true).first();
8538         
8539         var cw = ctr.getWidth();
8540         
8541         if (tbd) {
8542             
8543             tbd.setWidth(ctr.getWidth());
8544             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8545             // this needs fixing for various usage - currently only hydra job advers I think..
8546             //tdb.setHeight(
8547             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8548             //); 
8549             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8550             cw -= barsize;
8551         }
8552         cw = Math.max(cw, this.totalWidth);
8553         this.getGridEl().select('tr',true).setWidth(cw);
8554         // resize 'expandable coloumn?
8555         
8556         return; // we doe not have a view in this design..
8557         
8558     },
8559     onBodyScroll: function()
8560     {
8561         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8562         if(this.mainHead){
8563             this.mainHead.setStyle({
8564                 'position' : 'relative',
8565                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8566             });
8567         }
8568         
8569         if(this.lazyLoad){
8570             
8571             var scrollHeight = this.mainBody.dom.scrollHeight;
8572             
8573             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8574             
8575             var height = this.mainBody.getHeight();
8576             
8577             if(scrollHeight - height == scrollTop) {
8578                 
8579                 var total = this.ds.getTotalCount();
8580                 
8581                 if(this.footer.cursor + this.footer.pageSize < total){
8582                     
8583                     this.footer.ds.load({
8584                         params : {
8585                             start : this.footer.cursor + this.footer.pageSize,
8586                             limit : this.footer.pageSize
8587                         },
8588                         add : true
8589                     });
8590                 }
8591             }
8592             
8593         }
8594     },
8595     
8596     onHeaderChange : function()
8597     {
8598         var header = this.renderHeader();
8599         var table = this.el.select('table', true).first();
8600         
8601         this.mainHead.remove();
8602         this.mainHead = table.createChild(header, this.mainBody, false);
8603     },
8604     
8605     onHiddenChange : function(colModel, colIndex, hidden)
8606     {
8607         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8608         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8609         
8610         this.CSS.updateRule(thSelector, "display", "");
8611         this.CSS.updateRule(tdSelector, "display", "");
8612         
8613         if(hidden){
8614             this.CSS.updateRule(thSelector, "display", "none");
8615             this.CSS.updateRule(tdSelector, "display", "none");
8616         }
8617         
8618         this.onHeaderChange();
8619         this.onLoad();
8620     },
8621     
8622     setColumnWidth: function(col_index, width)
8623     {
8624         // width = "md-2 xs-2..."
8625         if(!this.colModel.config[col_index]) {
8626             return;
8627         }
8628         
8629         var w = width.split(" ");
8630         
8631         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8632         
8633         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8634         
8635         
8636         for(var j = 0; j < w.length; j++) {
8637             
8638             if(!w[j]) {
8639                 continue;
8640             }
8641             
8642             var size_cls = w[j].split("-");
8643             
8644             if(!Number.isInteger(size_cls[1] * 1)) {
8645                 continue;
8646             }
8647             
8648             if(!this.colModel.config[col_index][size_cls[0]]) {
8649                 continue;
8650             }
8651             
8652             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8653                 continue;
8654             }
8655             
8656             h_row[0].classList.replace(
8657                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8658                 "col-"+size_cls[0]+"-"+size_cls[1]
8659             );
8660             
8661             for(var i = 0; i < rows.length; i++) {
8662                 
8663                 var size_cls = w[j].split("-");
8664                 
8665                 if(!Number.isInteger(size_cls[1] * 1)) {
8666                     continue;
8667                 }
8668                 
8669                 if(!this.colModel.config[col_index][size_cls[0]]) {
8670                     continue;
8671                 }
8672                 
8673                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8674                     continue;
8675                 }
8676                 
8677                 rows[i].classList.replace(
8678                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8679                     "col-"+size_cls[0]+"-"+size_cls[1]
8680                 );
8681             }
8682             
8683             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8684         }
8685     }
8686 });
8687
8688  
8689
8690  /*
8691  * - LGPL
8692  *
8693  * table cell
8694  * 
8695  */
8696
8697 /**
8698  * @class Roo.bootstrap.TableCell
8699  * @extends Roo.bootstrap.Component
8700  * Bootstrap TableCell class
8701  * @cfg {String} html cell contain text
8702  * @cfg {String} cls cell class
8703  * @cfg {String} tag cell tag (td|th) default td
8704  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8705  * @cfg {String} align Aligns the content in a cell
8706  * @cfg {String} axis Categorizes cells
8707  * @cfg {String} bgcolor Specifies the background color of a cell
8708  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8709  * @cfg {Number} colspan Specifies the number of columns a cell should span
8710  * @cfg {String} headers Specifies one or more header cells a cell is related to
8711  * @cfg {Number} height Sets the height of a cell
8712  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8713  * @cfg {Number} rowspan Sets the number of rows a cell should span
8714  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8715  * @cfg {String} valign Vertical aligns the content in a cell
8716  * @cfg {Number} width Specifies the width of a cell
8717  * 
8718  * @constructor
8719  * Create a new TableCell
8720  * @param {Object} config The config object
8721  */
8722
8723 Roo.bootstrap.TableCell = function(config){
8724     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8725 };
8726
8727 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8728     
8729     html: false,
8730     cls: false,
8731     tag: false,
8732     abbr: false,
8733     align: false,
8734     axis: false,
8735     bgcolor: false,
8736     charoff: false,
8737     colspan: false,
8738     headers: false,
8739     height: false,
8740     nowrap: false,
8741     rowspan: false,
8742     scope: false,
8743     valign: false,
8744     width: false,
8745     
8746     
8747     getAutoCreate : function(){
8748         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8749         
8750         cfg = {
8751             tag: 'td'
8752         };
8753         
8754         if(this.tag){
8755             cfg.tag = this.tag;
8756         }
8757         
8758         if (this.html) {
8759             cfg.html=this.html
8760         }
8761         if (this.cls) {
8762             cfg.cls=this.cls
8763         }
8764         if (this.abbr) {
8765             cfg.abbr=this.abbr
8766         }
8767         if (this.align) {
8768             cfg.align=this.align
8769         }
8770         if (this.axis) {
8771             cfg.axis=this.axis
8772         }
8773         if (this.bgcolor) {
8774             cfg.bgcolor=this.bgcolor
8775         }
8776         if (this.charoff) {
8777             cfg.charoff=this.charoff
8778         }
8779         if (this.colspan) {
8780             cfg.colspan=this.colspan
8781         }
8782         if (this.headers) {
8783             cfg.headers=this.headers
8784         }
8785         if (this.height) {
8786             cfg.height=this.height
8787         }
8788         if (this.nowrap) {
8789             cfg.nowrap=this.nowrap
8790         }
8791         if (this.rowspan) {
8792             cfg.rowspan=this.rowspan
8793         }
8794         if (this.scope) {
8795             cfg.scope=this.scope
8796         }
8797         if (this.valign) {
8798             cfg.valign=this.valign
8799         }
8800         if (this.width) {
8801             cfg.width=this.width
8802         }
8803         
8804         
8805         return cfg;
8806     }
8807    
8808 });
8809
8810  
8811
8812  /*
8813  * - LGPL
8814  *
8815  * table row
8816  * 
8817  */
8818
8819 /**
8820  * @class Roo.bootstrap.TableRow
8821  * @extends Roo.bootstrap.Component
8822  * Bootstrap TableRow class
8823  * @cfg {String} cls row class
8824  * @cfg {String} align Aligns the content in a table row
8825  * @cfg {String} bgcolor Specifies a background color for a table row
8826  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8827  * @cfg {String} valign Vertical aligns the content in a table row
8828  * 
8829  * @constructor
8830  * Create a new TableRow
8831  * @param {Object} config The config object
8832  */
8833
8834 Roo.bootstrap.TableRow = function(config){
8835     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8836 };
8837
8838 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8839     
8840     cls: false,
8841     align: false,
8842     bgcolor: false,
8843     charoff: false,
8844     valign: false,
8845     
8846     getAutoCreate : function(){
8847         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8848         
8849         cfg = {
8850             tag: 'tr'
8851         };
8852             
8853         if(this.cls){
8854             cfg.cls = this.cls;
8855         }
8856         if(this.align){
8857             cfg.align = this.align;
8858         }
8859         if(this.bgcolor){
8860             cfg.bgcolor = this.bgcolor;
8861         }
8862         if(this.charoff){
8863             cfg.charoff = this.charoff;
8864         }
8865         if(this.valign){
8866             cfg.valign = this.valign;
8867         }
8868         
8869         return cfg;
8870     }
8871    
8872 });
8873
8874  
8875
8876  /*
8877  * - LGPL
8878  *
8879  * table body
8880  * 
8881  */
8882
8883 /**
8884  * @class Roo.bootstrap.TableBody
8885  * @extends Roo.bootstrap.Component
8886  * Bootstrap TableBody class
8887  * @cfg {String} cls element class
8888  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8889  * @cfg {String} align Aligns the content inside the element
8890  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8891  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8892  * 
8893  * @constructor
8894  * Create a new TableBody
8895  * @param {Object} config The config object
8896  */
8897
8898 Roo.bootstrap.TableBody = function(config){
8899     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8900 };
8901
8902 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8903     
8904     cls: false,
8905     tag: false,
8906     align: false,
8907     charoff: false,
8908     valign: false,
8909     
8910     getAutoCreate : function(){
8911         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8912         
8913         cfg = {
8914             tag: 'tbody'
8915         };
8916             
8917         if (this.cls) {
8918             cfg.cls=this.cls
8919         }
8920         if(this.tag){
8921             cfg.tag = this.tag;
8922         }
8923         
8924         if(this.align){
8925             cfg.align = this.align;
8926         }
8927         if(this.charoff){
8928             cfg.charoff = this.charoff;
8929         }
8930         if(this.valign){
8931             cfg.valign = this.valign;
8932         }
8933         
8934         return cfg;
8935     }
8936     
8937     
8938 //    initEvents : function()
8939 //    {
8940 //        
8941 //        if(!this.store){
8942 //            return;
8943 //        }
8944 //        
8945 //        this.store = Roo.factory(this.store, Roo.data);
8946 //        this.store.on('load', this.onLoad, this);
8947 //        
8948 //        this.store.load();
8949 //        
8950 //    },
8951 //    
8952 //    onLoad: function () 
8953 //    {   
8954 //        this.fireEvent('load', this);
8955 //    }
8956 //    
8957 //   
8958 });
8959
8960  
8961
8962  /*
8963  * Based on:
8964  * Ext JS Library 1.1.1
8965  * Copyright(c) 2006-2007, Ext JS, LLC.
8966  *
8967  * Originally Released Under LGPL - original licence link has changed is not relivant.
8968  *
8969  * Fork - LGPL
8970  * <script type="text/javascript">
8971  */
8972
8973 // as we use this in bootstrap.
8974 Roo.namespace('Roo.form');
8975  /**
8976  * @class Roo.form.Action
8977  * Internal Class used to handle form actions
8978  * @constructor
8979  * @param {Roo.form.BasicForm} el The form element or its id
8980  * @param {Object} config Configuration options
8981  */
8982
8983  
8984  
8985 // define the action interface
8986 Roo.form.Action = function(form, options){
8987     this.form = form;
8988     this.options = options || {};
8989 };
8990 /**
8991  * Client Validation Failed
8992  * @const 
8993  */
8994 Roo.form.Action.CLIENT_INVALID = 'client';
8995 /**
8996  * Server Validation Failed
8997  * @const 
8998  */
8999 Roo.form.Action.SERVER_INVALID = 'server';
9000  /**
9001  * Connect to Server Failed
9002  * @const 
9003  */
9004 Roo.form.Action.CONNECT_FAILURE = 'connect';
9005 /**
9006  * Reading Data from Server Failed
9007  * @const 
9008  */
9009 Roo.form.Action.LOAD_FAILURE = 'load';
9010
9011 Roo.form.Action.prototype = {
9012     type : 'default',
9013     failureType : undefined,
9014     response : undefined,
9015     result : undefined,
9016
9017     // interface method
9018     run : function(options){
9019
9020     },
9021
9022     // interface method
9023     success : function(response){
9024
9025     },
9026
9027     // interface method
9028     handleResponse : function(response){
9029
9030     },
9031
9032     // default connection failure
9033     failure : function(response){
9034         
9035         this.response = response;
9036         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9037         this.form.afterAction(this, false);
9038     },
9039
9040     processResponse : function(response){
9041         this.response = response;
9042         if(!response.responseText){
9043             return true;
9044         }
9045         this.result = this.handleResponse(response);
9046         return this.result;
9047     },
9048
9049     // utility functions used internally
9050     getUrl : function(appendParams){
9051         var url = this.options.url || this.form.url || this.form.el.dom.action;
9052         if(appendParams){
9053             var p = this.getParams();
9054             if(p){
9055                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9056             }
9057         }
9058         return url;
9059     },
9060
9061     getMethod : function(){
9062         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9063     },
9064
9065     getParams : function(){
9066         var bp = this.form.baseParams;
9067         var p = this.options.params;
9068         if(p){
9069             if(typeof p == "object"){
9070                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9071             }else if(typeof p == 'string' && bp){
9072                 p += '&' + Roo.urlEncode(bp);
9073             }
9074         }else if(bp){
9075             p = Roo.urlEncode(bp);
9076         }
9077         return p;
9078     },
9079
9080     createCallback : function(){
9081         return {
9082             success: this.success,
9083             failure: this.failure,
9084             scope: this,
9085             timeout: (this.form.timeout*1000),
9086             upload: this.form.fileUpload ? this.success : undefined
9087         };
9088     }
9089 };
9090
9091 Roo.form.Action.Submit = function(form, options){
9092     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9093 };
9094
9095 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9096     type : 'submit',
9097
9098     haveProgress : false,
9099     uploadComplete : false,
9100     
9101     // uploadProgress indicator.
9102     uploadProgress : function()
9103     {
9104         if (!this.form.progressUrl) {
9105             return;
9106         }
9107         
9108         if (!this.haveProgress) {
9109             Roo.MessageBox.progress("Uploading", "Uploading");
9110         }
9111         if (this.uploadComplete) {
9112            Roo.MessageBox.hide();
9113            return;
9114         }
9115         
9116         this.haveProgress = true;
9117    
9118         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9119         
9120         var c = new Roo.data.Connection();
9121         c.request({
9122             url : this.form.progressUrl,
9123             params: {
9124                 id : uid
9125             },
9126             method: 'GET',
9127             success : function(req){
9128                //console.log(data);
9129                 var rdata = false;
9130                 var edata;
9131                 try  {
9132                    rdata = Roo.decode(req.responseText)
9133                 } catch (e) {
9134                     Roo.log("Invalid data from server..");
9135                     Roo.log(edata);
9136                     return;
9137                 }
9138                 if (!rdata || !rdata.success) {
9139                     Roo.log(rdata);
9140                     Roo.MessageBox.alert(Roo.encode(rdata));
9141                     return;
9142                 }
9143                 var data = rdata.data;
9144                 
9145                 if (this.uploadComplete) {
9146                    Roo.MessageBox.hide();
9147                    return;
9148                 }
9149                    
9150                 if (data){
9151                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9152                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9153                     );
9154                 }
9155                 this.uploadProgress.defer(2000,this);
9156             },
9157        
9158             failure: function(data) {
9159                 Roo.log('progress url failed ');
9160                 Roo.log(data);
9161             },
9162             scope : this
9163         });
9164            
9165     },
9166     
9167     
9168     run : function()
9169     {
9170         // run get Values on the form, so it syncs any secondary forms.
9171         this.form.getValues();
9172         
9173         var o = this.options;
9174         var method = this.getMethod();
9175         var isPost = method == 'POST';
9176         if(o.clientValidation === false || this.form.isValid()){
9177             
9178             if (this.form.progressUrl) {
9179                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9180                     (new Date() * 1) + '' + Math.random());
9181                     
9182             } 
9183             
9184             
9185             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9186                 form:this.form.el.dom,
9187                 url:this.getUrl(!isPost),
9188                 method: method,
9189                 params:isPost ? this.getParams() : null,
9190                 isUpload: this.form.fileUpload,
9191                 formData : this.form.formData
9192             }));
9193             
9194             this.uploadProgress();
9195
9196         }else if (o.clientValidation !== false){ // client validation failed
9197             this.failureType = Roo.form.Action.CLIENT_INVALID;
9198             this.form.afterAction(this, false);
9199         }
9200     },
9201
9202     success : function(response)
9203     {
9204         this.uploadComplete= true;
9205         if (this.haveProgress) {
9206             Roo.MessageBox.hide();
9207         }
9208         
9209         
9210         var result = this.processResponse(response);
9211         if(result === true || result.success){
9212             this.form.afterAction(this, true);
9213             return;
9214         }
9215         if(result.errors){
9216             this.form.markInvalid(result.errors);
9217             this.failureType = Roo.form.Action.SERVER_INVALID;
9218         }
9219         this.form.afterAction(this, false);
9220     },
9221     failure : function(response)
9222     {
9223         this.uploadComplete= true;
9224         if (this.haveProgress) {
9225             Roo.MessageBox.hide();
9226         }
9227         
9228         this.response = response;
9229         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9230         this.form.afterAction(this, false);
9231     },
9232     
9233     handleResponse : function(response){
9234         if(this.form.errorReader){
9235             var rs = this.form.errorReader.read(response);
9236             var errors = [];
9237             if(rs.records){
9238                 for(var i = 0, len = rs.records.length; i < len; i++) {
9239                     var r = rs.records[i];
9240                     errors[i] = r.data;
9241                 }
9242             }
9243             if(errors.length < 1){
9244                 errors = null;
9245             }
9246             return {
9247                 success : rs.success,
9248                 errors : errors
9249             };
9250         }
9251         var ret = false;
9252         try {
9253             ret = Roo.decode(response.responseText);
9254         } catch (e) {
9255             ret = {
9256                 success: false,
9257                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9258                 errors : []
9259             };
9260         }
9261         return ret;
9262         
9263     }
9264 });
9265
9266
9267 Roo.form.Action.Load = function(form, options){
9268     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9269     this.reader = this.form.reader;
9270 };
9271
9272 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9273     type : 'load',
9274
9275     run : function(){
9276         
9277         Roo.Ajax.request(Roo.apply(
9278                 this.createCallback(), {
9279                     method:this.getMethod(),
9280                     url:this.getUrl(false),
9281                     params:this.getParams()
9282         }));
9283     },
9284
9285     success : function(response){
9286         
9287         var result = this.processResponse(response);
9288         if(result === true || !result.success || !result.data){
9289             this.failureType = Roo.form.Action.LOAD_FAILURE;
9290             this.form.afterAction(this, false);
9291             return;
9292         }
9293         this.form.clearInvalid();
9294         this.form.setValues(result.data);
9295         this.form.afterAction(this, true);
9296     },
9297
9298     handleResponse : function(response){
9299         if(this.form.reader){
9300             var rs = this.form.reader.read(response);
9301             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9302             return {
9303                 success : rs.success,
9304                 data : data
9305             };
9306         }
9307         return Roo.decode(response.responseText);
9308     }
9309 });
9310
9311 Roo.form.Action.ACTION_TYPES = {
9312     'load' : Roo.form.Action.Load,
9313     'submit' : Roo.form.Action.Submit
9314 };/*
9315  * - LGPL
9316  *
9317  * form
9318  *
9319  */
9320
9321 /**
9322  * @class Roo.bootstrap.Form
9323  * @extends Roo.bootstrap.Component
9324  * Bootstrap Form class
9325  * @cfg {String} method  GET | POST (default POST)
9326  * @cfg {String} labelAlign top | left (default top)
9327  * @cfg {String} align left  | right - for navbars
9328  * @cfg {Boolean} loadMask load mask when submit (default true)
9329
9330  *
9331  * @constructor
9332  * Create a new Form
9333  * @param {Object} config The config object
9334  */
9335
9336
9337 Roo.bootstrap.Form = function(config){
9338     
9339     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9340     
9341     Roo.bootstrap.Form.popover.apply();
9342     
9343     this.addEvents({
9344         /**
9345          * @event clientvalidation
9346          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9347          * @param {Form} this
9348          * @param {Boolean} valid true if the form has passed client-side validation
9349          */
9350         clientvalidation: true,
9351         /**
9352          * @event beforeaction
9353          * Fires before any action is performed. Return false to cancel the action.
9354          * @param {Form} this
9355          * @param {Action} action The action to be performed
9356          */
9357         beforeaction: true,
9358         /**
9359          * @event actionfailed
9360          * Fires when an action fails.
9361          * @param {Form} this
9362          * @param {Action} action The action that failed
9363          */
9364         actionfailed : true,
9365         /**
9366          * @event actioncomplete
9367          * Fires when an action is completed.
9368          * @param {Form} this
9369          * @param {Action} action The action that completed
9370          */
9371         actioncomplete : true
9372     });
9373 };
9374
9375 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9376
9377      /**
9378      * @cfg {String} method
9379      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9380      */
9381     method : 'POST',
9382     /**
9383      * @cfg {String} url
9384      * The URL to use for form actions if one isn't supplied in the action options.
9385      */
9386     /**
9387      * @cfg {Boolean} fileUpload
9388      * Set to true if this form is a file upload.
9389      */
9390
9391     /**
9392      * @cfg {Object} baseParams
9393      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9394      */
9395
9396     /**
9397      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9398      */
9399     timeout: 30,
9400     /**
9401      * @cfg {Sting} align (left|right) for navbar forms
9402      */
9403     align : 'left',
9404
9405     // private
9406     activeAction : null,
9407
9408     /**
9409      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9410      * element by passing it or its id or mask the form itself by passing in true.
9411      * @type Mixed
9412      */
9413     waitMsgTarget : false,
9414
9415     loadMask : true,
9416     
9417     /**
9418      * @cfg {Boolean} errorMask (true|false) default false
9419      */
9420     errorMask : false,
9421     
9422     /**
9423      * @cfg {Number} maskOffset Default 100
9424      */
9425     maskOffset : 100,
9426     
9427     /**
9428      * @cfg {Boolean} maskBody
9429      */
9430     maskBody : false,
9431
9432     getAutoCreate : function(){
9433
9434         var cfg = {
9435             tag: 'form',
9436             method : this.method || 'POST',
9437             id : this.id || Roo.id(),
9438             cls : ''
9439         };
9440         if (this.parent().xtype.match(/^Nav/)) {
9441             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9442
9443         }
9444
9445         if (this.labelAlign == 'left' ) {
9446             cfg.cls += ' form-horizontal';
9447         }
9448
9449
9450         return cfg;
9451     },
9452     initEvents : function()
9453     {
9454         this.el.on('submit', this.onSubmit, this);
9455         // this was added as random key presses on the form where triggering form submit.
9456         this.el.on('keypress', function(e) {
9457             if (e.getCharCode() != 13) {
9458                 return true;
9459             }
9460             // we might need to allow it for textareas.. and some other items.
9461             // check e.getTarget().
9462
9463             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9464                 return true;
9465             }
9466
9467             Roo.log("keypress blocked");
9468
9469             e.preventDefault();
9470             return false;
9471         });
9472         
9473     },
9474     // private
9475     onSubmit : function(e){
9476         e.stopEvent();
9477     },
9478
9479      /**
9480      * Returns true if client-side validation on the form is successful.
9481      * @return Boolean
9482      */
9483     isValid : function(){
9484         var items = this.getItems();
9485         var valid = true;
9486         var target = false;
9487         
9488         items.each(function(f){
9489             
9490             if(f.validate()){
9491                 return;
9492             }
9493             
9494             Roo.log('invalid field: ' + f.name);
9495             
9496             valid = false;
9497
9498             if(!target && f.el.isVisible(true)){
9499                 target = f;
9500             }
9501            
9502         });
9503         
9504         if(this.errorMask && !valid){
9505             Roo.bootstrap.Form.popover.mask(this, target);
9506         }
9507         
9508         return valid;
9509     },
9510     
9511     /**
9512      * Returns true if any fields in this form have changed since their original load.
9513      * @return Boolean
9514      */
9515     isDirty : function(){
9516         var dirty = false;
9517         var items = this.getItems();
9518         items.each(function(f){
9519            if(f.isDirty()){
9520                dirty = true;
9521                return false;
9522            }
9523            return true;
9524         });
9525         return dirty;
9526     },
9527      /**
9528      * Performs a predefined action (submit or load) or custom actions you define on this form.
9529      * @param {String} actionName The name of the action type
9530      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9531      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9532      * accept other config options):
9533      * <pre>
9534 Property          Type             Description
9535 ----------------  ---------------  ----------------------------------------------------------------------------------
9536 url               String           The url for the action (defaults to the form's url)
9537 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9538 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9539 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9540                                    validate the form on the client (defaults to false)
9541      * </pre>
9542      * @return {BasicForm} this
9543      */
9544     doAction : function(action, options){
9545         if(typeof action == 'string'){
9546             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9547         }
9548         if(this.fireEvent('beforeaction', this, action) !== false){
9549             this.beforeAction(action);
9550             action.run.defer(100, action);
9551         }
9552         return this;
9553     },
9554
9555     // private
9556     beforeAction : function(action){
9557         var o = action.options;
9558         
9559         if(this.loadMask){
9560             
9561             if(this.maskBody){
9562                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9563             } else {
9564                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9565             }
9566         }
9567         // not really supported yet.. ??
9568
9569         //if(this.waitMsgTarget === true){
9570         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9571         //}else if(this.waitMsgTarget){
9572         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9573         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9574         //}else {
9575         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9576        // }
9577
9578     },
9579
9580     // private
9581     afterAction : function(action, success){
9582         this.activeAction = null;
9583         var o = action.options;
9584
9585         if(this.loadMask){
9586             
9587             if(this.maskBody){
9588                 Roo.get(document.body).unmask();
9589             } else {
9590                 this.el.unmask();
9591             }
9592         }
9593         
9594         //if(this.waitMsgTarget === true){
9595 //            this.el.unmask();
9596         //}else if(this.waitMsgTarget){
9597         //    this.waitMsgTarget.unmask();
9598         //}else{
9599         //    Roo.MessageBox.updateProgress(1);
9600         //    Roo.MessageBox.hide();
9601        // }
9602         //
9603         if(success){
9604             if(o.reset){
9605                 this.reset();
9606             }
9607             Roo.callback(o.success, o.scope, [this, action]);
9608             this.fireEvent('actioncomplete', this, action);
9609
9610         }else{
9611
9612             // failure condition..
9613             // we have a scenario where updates need confirming.
9614             // eg. if a locking scenario exists..
9615             // we look for { errors : { needs_confirm : true }} in the response.
9616             if (
9617                 (typeof(action.result) != 'undefined')  &&
9618                 (typeof(action.result.errors) != 'undefined')  &&
9619                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9620            ){
9621                 var _t = this;
9622                 Roo.log("not supported yet");
9623                  /*
9624
9625                 Roo.MessageBox.confirm(
9626                     "Change requires confirmation",
9627                     action.result.errorMsg,
9628                     function(r) {
9629                         if (r != 'yes') {
9630                             return;
9631                         }
9632                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9633                     }
9634
9635                 );
9636                 */
9637
9638
9639                 return;
9640             }
9641
9642             Roo.callback(o.failure, o.scope, [this, action]);
9643             // show an error message if no failed handler is set..
9644             if (!this.hasListener('actionfailed')) {
9645                 Roo.log("need to add dialog support");
9646                 /*
9647                 Roo.MessageBox.alert("Error",
9648                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9649                         action.result.errorMsg :
9650                         "Saving Failed, please check your entries or try again"
9651                 );
9652                 */
9653             }
9654
9655             this.fireEvent('actionfailed', this, action);
9656         }
9657
9658     },
9659     /**
9660      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9661      * @param {String} id The value to search for
9662      * @return Field
9663      */
9664     findField : function(id){
9665         var items = this.getItems();
9666         var field = items.get(id);
9667         if(!field){
9668              items.each(function(f){
9669                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9670                     field = f;
9671                     return false;
9672                 }
9673                 return true;
9674             });
9675         }
9676         return field || null;
9677     },
9678      /**
9679      * Mark fields in this form invalid in bulk.
9680      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9681      * @return {BasicForm} this
9682      */
9683     markInvalid : function(errors){
9684         if(errors instanceof Array){
9685             for(var i = 0, len = errors.length; i < len; i++){
9686                 var fieldError = errors[i];
9687                 var f = this.findField(fieldError.id);
9688                 if(f){
9689                     f.markInvalid(fieldError.msg);
9690                 }
9691             }
9692         }else{
9693             var field, id;
9694             for(id in errors){
9695                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9696                     field.markInvalid(errors[id]);
9697                 }
9698             }
9699         }
9700         //Roo.each(this.childForms || [], function (f) {
9701         //    f.markInvalid(errors);
9702         //});
9703
9704         return this;
9705     },
9706
9707     /**
9708      * Set values for fields in this form in bulk.
9709      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9710      * @return {BasicForm} this
9711      */
9712     setValues : function(values){
9713         if(values instanceof Array){ // array of objects
9714             for(var i = 0, len = values.length; i < len; i++){
9715                 var v = values[i];
9716                 var f = this.findField(v.id);
9717                 if(f){
9718                     f.setValue(v.value);
9719                     if(this.trackResetOnLoad){
9720                         f.originalValue = f.getValue();
9721                     }
9722                 }
9723             }
9724         }else{ // object hash
9725             var field, id;
9726             for(id in values){
9727                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9728
9729                     if (field.setFromData &&
9730                         field.valueField &&
9731                         field.displayField &&
9732                         // combos' with local stores can
9733                         // be queried via setValue()
9734                         // to set their value..
9735                         (field.store && !field.store.isLocal)
9736                         ) {
9737                         // it's a combo
9738                         var sd = { };
9739                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9740                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9741                         field.setFromData(sd);
9742
9743                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9744                         
9745                         field.setFromData(values);
9746                         
9747                     } else {
9748                         field.setValue(values[id]);
9749                     }
9750
9751
9752                     if(this.trackResetOnLoad){
9753                         field.originalValue = field.getValue();
9754                     }
9755                 }
9756             }
9757         }
9758
9759         //Roo.each(this.childForms || [], function (f) {
9760         //    f.setValues(values);
9761         //});
9762
9763         return this;
9764     },
9765
9766     /**
9767      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9768      * they are returned as an array.
9769      * @param {Boolean} asString
9770      * @return {Object}
9771      */
9772     getValues : function(asString){
9773         //if (this.childForms) {
9774             // copy values from the child forms
9775         //    Roo.each(this.childForms, function (f) {
9776         //        this.setValues(f.getValues());
9777         //    }, this);
9778         //}
9779
9780
9781
9782         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9783         if(asString === true){
9784             return fs;
9785         }
9786         return Roo.urlDecode(fs);
9787     },
9788
9789     /**
9790      * Returns the fields in this form as an object with key/value pairs.
9791      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9792      * @return {Object}
9793      */
9794     getFieldValues : function(with_hidden)
9795     {
9796         var items = this.getItems();
9797         var ret = {};
9798         items.each(function(f){
9799             
9800             if (!f.getName()) {
9801                 return;
9802             }
9803             
9804             var v = f.getValue();
9805             
9806             if (f.inputType =='radio') {
9807                 if (typeof(ret[f.getName()]) == 'undefined') {
9808                     ret[f.getName()] = ''; // empty..
9809                 }
9810
9811                 if (!f.el.dom.checked) {
9812                     return;
9813
9814                 }
9815                 v = f.el.dom.value;
9816
9817             }
9818             
9819             if(f.xtype == 'MoneyField'){
9820                 ret[f.currencyName] = f.getCurrency();
9821             }
9822
9823             // not sure if this supported any more..
9824             if ((typeof(v) == 'object') && f.getRawValue) {
9825                 v = f.getRawValue() ; // dates..
9826             }
9827             // combo boxes where name != hiddenName...
9828             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9829                 ret[f.name] = f.getRawValue();
9830             }
9831             ret[f.getName()] = v;
9832         });
9833
9834         return ret;
9835     },
9836
9837     /**
9838      * Clears all invalid messages in this form.
9839      * @return {BasicForm} this
9840      */
9841     clearInvalid : function(){
9842         var items = this.getItems();
9843
9844         items.each(function(f){
9845            f.clearInvalid();
9846         });
9847
9848         return this;
9849     },
9850
9851     /**
9852      * Resets this form.
9853      * @return {BasicForm} this
9854      */
9855     reset : function(){
9856         var items = this.getItems();
9857         items.each(function(f){
9858             f.reset();
9859         });
9860
9861         Roo.each(this.childForms || [], function (f) {
9862             f.reset();
9863         });
9864
9865
9866         return this;
9867     },
9868     
9869     getItems : function()
9870     {
9871         var r=new Roo.util.MixedCollection(false, function(o){
9872             return o.id || (o.id = Roo.id());
9873         });
9874         var iter = function(el) {
9875             if (el.inputEl) {
9876                 r.add(el);
9877             }
9878             if (!el.items) {
9879                 return;
9880             }
9881             Roo.each(el.items,function(e) {
9882                 iter(e);
9883             });
9884         };
9885
9886         iter(this);
9887         return r;
9888     },
9889     
9890     hideFields : function(items)
9891     {
9892         Roo.each(items, function(i){
9893             
9894             var f = this.findField(i);
9895             
9896             if(!f){
9897                 return;
9898             }
9899             
9900             f.hide();
9901             
9902         }, this);
9903     },
9904     
9905     showFields : function(items)
9906     {
9907         Roo.each(items, function(i){
9908             
9909             var f = this.findField(i);
9910             
9911             if(!f){
9912                 return;
9913             }
9914             
9915             f.show();
9916             
9917         }, this);
9918     }
9919
9920 });
9921
9922 Roo.apply(Roo.bootstrap.Form, {
9923     
9924     popover : {
9925         
9926         padding : 5,
9927         
9928         isApplied : false,
9929         
9930         isMasked : false,
9931         
9932         form : false,
9933         
9934         target : false,
9935         
9936         toolTip : false,
9937         
9938         intervalID : false,
9939         
9940         maskEl : false,
9941         
9942         apply : function()
9943         {
9944             if(this.isApplied){
9945                 return;
9946             }
9947             
9948             this.maskEl = {
9949                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9950                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
9951                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
9952                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
9953             };
9954             
9955             this.maskEl.top.enableDisplayMode("block");
9956             this.maskEl.left.enableDisplayMode("block");
9957             this.maskEl.bottom.enableDisplayMode("block");
9958             this.maskEl.right.enableDisplayMode("block");
9959             
9960             this.toolTip = new Roo.bootstrap.Tooltip({
9961                 cls : 'roo-form-error-popover',
9962                 alignment : {
9963                     'left' : ['r-l', [-2,0], 'right'],
9964                     'right' : ['l-r', [2,0], 'left'],
9965                     'bottom' : ['tl-bl', [0,2], 'top'],
9966                     'top' : [ 'bl-tl', [0,-2], 'bottom']
9967                 }
9968             });
9969             
9970             this.toolTip.render(Roo.get(document.body));
9971
9972             this.toolTip.el.enableDisplayMode("block");
9973             
9974             Roo.get(document.body).on('click', function(){
9975                 this.unmask();
9976             }, this);
9977             
9978             Roo.get(document.body).on('touchstart', function(){
9979                 this.unmask();
9980             }, this);
9981             
9982             this.isApplied = true
9983         },
9984         
9985         mask : function(form, target)
9986         {
9987             this.form = form;
9988             
9989             this.target = target;
9990             
9991             if(!this.form.errorMask || !target.el){
9992                 return;
9993             }
9994             
9995             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
9996             
9997             Roo.log(scrollable);
9998             
9999             var ot = this.target.el.calcOffsetsTo(scrollable);
10000             
10001             var scrollTo = ot[1] - this.form.maskOffset;
10002             
10003             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10004             
10005             scrollable.scrollTo('top', scrollTo);
10006             
10007             var box = this.target.el.getBox();
10008             Roo.log(box);
10009             var zIndex = Roo.bootstrap.Modal.zIndex++;
10010
10011             
10012             this.maskEl.top.setStyle('position', 'absolute');
10013             this.maskEl.top.setStyle('z-index', zIndex);
10014             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10015             this.maskEl.top.setLeft(0);
10016             this.maskEl.top.setTop(0);
10017             this.maskEl.top.show();
10018             
10019             this.maskEl.left.setStyle('position', 'absolute');
10020             this.maskEl.left.setStyle('z-index', zIndex);
10021             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10022             this.maskEl.left.setLeft(0);
10023             this.maskEl.left.setTop(box.y - this.padding);
10024             this.maskEl.left.show();
10025
10026             this.maskEl.bottom.setStyle('position', 'absolute');
10027             this.maskEl.bottom.setStyle('z-index', zIndex);
10028             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10029             this.maskEl.bottom.setLeft(0);
10030             this.maskEl.bottom.setTop(box.bottom + this.padding);
10031             this.maskEl.bottom.show();
10032
10033             this.maskEl.right.setStyle('position', 'absolute');
10034             this.maskEl.right.setStyle('z-index', zIndex);
10035             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10036             this.maskEl.right.setLeft(box.right + this.padding);
10037             this.maskEl.right.setTop(box.y - this.padding);
10038             this.maskEl.right.show();
10039
10040             this.toolTip.bindEl = this.target.el;
10041
10042             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10043
10044             var tip = this.target.blankText;
10045
10046             if(this.target.getValue() !== '' ) {
10047                 
10048                 if (this.target.invalidText.length) {
10049                     tip = this.target.invalidText;
10050                 } else if (this.target.regexText.length){
10051                     tip = this.target.regexText;
10052                 }
10053             }
10054
10055             this.toolTip.show(tip);
10056
10057             this.intervalID = window.setInterval(function() {
10058                 Roo.bootstrap.Form.popover.unmask();
10059             }, 10000);
10060
10061             window.onwheel = function(){ return false;};
10062             
10063             (function(){ this.isMasked = true; }).defer(500, this);
10064             
10065         },
10066         
10067         unmask : function()
10068         {
10069             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10070                 return;
10071             }
10072             
10073             this.maskEl.top.setStyle('position', 'absolute');
10074             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10075             this.maskEl.top.hide();
10076
10077             this.maskEl.left.setStyle('position', 'absolute');
10078             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10079             this.maskEl.left.hide();
10080
10081             this.maskEl.bottom.setStyle('position', 'absolute');
10082             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10083             this.maskEl.bottom.hide();
10084
10085             this.maskEl.right.setStyle('position', 'absolute');
10086             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10087             this.maskEl.right.hide();
10088             
10089             this.toolTip.hide();
10090             
10091             this.toolTip.el.hide();
10092             
10093             window.onwheel = function(){ return true;};
10094             
10095             if(this.intervalID){
10096                 window.clearInterval(this.intervalID);
10097                 this.intervalID = false;
10098             }
10099             
10100             this.isMasked = false;
10101             
10102         }
10103         
10104     }
10105     
10106 });
10107
10108 /*
10109  * Based on:
10110  * Ext JS Library 1.1.1
10111  * Copyright(c) 2006-2007, Ext JS, LLC.
10112  *
10113  * Originally Released Under LGPL - original licence link has changed is not relivant.
10114  *
10115  * Fork - LGPL
10116  * <script type="text/javascript">
10117  */
10118 /**
10119  * @class Roo.form.VTypes
10120  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10121  * @singleton
10122  */
10123 Roo.form.VTypes = function(){
10124     // closure these in so they are only created once.
10125     var alpha = /^[a-zA-Z_]+$/;
10126     var alphanum = /^[a-zA-Z0-9_]+$/;
10127     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10128     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10129
10130     // All these messages and functions are configurable
10131     return {
10132         /**
10133          * The function used to validate email addresses
10134          * @param {String} value The email address
10135          */
10136         'email' : function(v){
10137             return email.test(v);
10138         },
10139         /**
10140          * The error text to display when the email validation function returns false
10141          * @type String
10142          */
10143         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10144         /**
10145          * The keystroke filter mask to be applied on email input
10146          * @type RegExp
10147          */
10148         'emailMask' : /[a-z0-9_\.\-@]/i,
10149
10150         /**
10151          * The function used to validate URLs
10152          * @param {String} value The URL
10153          */
10154         'url' : function(v){
10155             return url.test(v);
10156         },
10157         /**
10158          * The error text to display when the url validation function returns false
10159          * @type String
10160          */
10161         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10162         
10163         /**
10164          * The function used to validate alpha values
10165          * @param {String} value The value
10166          */
10167         'alpha' : function(v){
10168             return alpha.test(v);
10169         },
10170         /**
10171          * The error text to display when the alpha validation function returns false
10172          * @type String
10173          */
10174         'alphaText' : 'This field should only contain letters and _',
10175         /**
10176          * The keystroke filter mask to be applied on alpha input
10177          * @type RegExp
10178          */
10179         'alphaMask' : /[a-z_]/i,
10180
10181         /**
10182          * The function used to validate alphanumeric values
10183          * @param {String} value The value
10184          */
10185         'alphanum' : function(v){
10186             return alphanum.test(v);
10187         },
10188         /**
10189          * The error text to display when the alphanumeric validation function returns false
10190          * @type String
10191          */
10192         'alphanumText' : 'This field should only contain letters, numbers and _',
10193         /**
10194          * The keystroke filter mask to be applied on alphanumeric input
10195          * @type RegExp
10196          */
10197         'alphanumMask' : /[a-z0-9_]/i
10198     };
10199 }();/*
10200  * - LGPL
10201  *
10202  * Input
10203  * 
10204  */
10205
10206 /**
10207  * @class Roo.bootstrap.Input
10208  * @extends Roo.bootstrap.Component
10209  * Bootstrap Input class
10210  * @cfg {Boolean} disabled is it disabled
10211  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10212  * @cfg {String} name name of the input
10213  * @cfg {string} fieldLabel - the label associated
10214  * @cfg {string} placeholder - placeholder to put in text.
10215  * @cfg {string}  before - input group add on before
10216  * @cfg {string} after - input group add on after
10217  * @cfg {string} size - (lg|sm) or leave empty..
10218  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10219  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10220  * @cfg {Number} md colspan out of 12 for computer-sized screens
10221  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10222  * @cfg {string} value default value of the input
10223  * @cfg {Number} labelWidth set the width of label 
10224  * @cfg {Number} labellg set the width of label (1-12)
10225  * @cfg {Number} labelmd set the width of label (1-12)
10226  * @cfg {Number} labelsm set the width of label (1-12)
10227  * @cfg {Number} labelxs set the width of label (1-12)
10228  * @cfg {String} labelAlign (top|left)
10229  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10230  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10231  * @cfg {String} indicatorpos (left|right) default left
10232  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10233  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10234  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10235
10236  * @cfg {String} align (left|center|right) Default left
10237  * @cfg {Boolean} forceFeedback (true|false) Default false
10238  * 
10239  * @constructor
10240  * Create a new Input
10241  * @param {Object} config The config object
10242  */
10243
10244 Roo.bootstrap.Input = function(config){
10245     
10246     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10247     
10248     this.addEvents({
10249         /**
10250          * @event focus
10251          * Fires when this field receives input focus.
10252          * @param {Roo.form.Field} this
10253          */
10254         focus : true,
10255         /**
10256          * @event blur
10257          * Fires when this field loses input focus.
10258          * @param {Roo.form.Field} this
10259          */
10260         blur : true,
10261         /**
10262          * @event specialkey
10263          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10264          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10265          * @param {Roo.form.Field} this
10266          * @param {Roo.EventObject} e The event object
10267          */
10268         specialkey : true,
10269         /**
10270          * @event change
10271          * Fires just before the field blurs if the field value has changed.
10272          * @param {Roo.form.Field} this
10273          * @param {Mixed} newValue The new value
10274          * @param {Mixed} oldValue The original value
10275          */
10276         change : true,
10277         /**
10278          * @event invalid
10279          * Fires after the field has been marked as invalid.
10280          * @param {Roo.form.Field} this
10281          * @param {String} msg The validation message
10282          */
10283         invalid : true,
10284         /**
10285          * @event valid
10286          * Fires after the field has been validated with no errors.
10287          * @param {Roo.form.Field} this
10288          */
10289         valid : true,
10290          /**
10291          * @event keyup
10292          * Fires after the key up
10293          * @param {Roo.form.Field} this
10294          * @param {Roo.EventObject}  e The event Object
10295          */
10296         keyup : true
10297     });
10298 };
10299
10300 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10301      /**
10302      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10303       automatic validation (defaults to "keyup").
10304      */
10305     validationEvent : "keyup",
10306      /**
10307      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10308      */
10309     validateOnBlur : true,
10310     /**
10311      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10312      */
10313     validationDelay : 250,
10314      /**
10315      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10316      */
10317     focusClass : "x-form-focus",  // not needed???
10318     
10319        
10320     /**
10321      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10322      */
10323     invalidClass : "has-warning",
10324     
10325     /**
10326      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10327      */
10328     validClass : "has-success",
10329     
10330     /**
10331      * @cfg {Boolean} hasFeedback (true|false) default true
10332      */
10333     hasFeedback : true,
10334     
10335     /**
10336      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10337      */
10338     invalidFeedbackClass : "glyphicon-warning-sign",
10339     
10340     /**
10341      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10342      */
10343     validFeedbackClass : "glyphicon-ok",
10344     
10345     /**
10346      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10347      */
10348     selectOnFocus : false,
10349     
10350      /**
10351      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10352      */
10353     maskRe : null,
10354        /**
10355      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10356      */
10357     vtype : null,
10358     
10359       /**
10360      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10361      */
10362     disableKeyFilter : false,
10363     
10364        /**
10365      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10366      */
10367     disabled : false,
10368      /**
10369      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10370      */
10371     allowBlank : true,
10372     /**
10373      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10374      */
10375     blankText : "Please complete this mandatory field",
10376     
10377      /**
10378      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10379      */
10380     minLength : 0,
10381     /**
10382      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10383      */
10384     maxLength : Number.MAX_VALUE,
10385     /**
10386      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10387      */
10388     minLengthText : "The minimum length for this field is {0}",
10389     /**
10390      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10391      */
10392     maxLengthText : "The maximum length for this field is {0}",
10393   
10394     
10395     /**
10396      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10397      * If available, this function will be called only after the basic validators all return true, and will be passed the
10398      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10399      */
10400     validator : null,
10401     /**
10402      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10403      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10404      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10405      */
10406     regex : null,
10407     /**
10408      * @cfg {String} regexText -- Depricated - use Invalid Text
10409      */
10410     regexText : "",
10411     
10412     /**
10413      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10414      */
10415     invalidText : "",
10416     
10417     
10418     
10419     autocomplete: false,
10420     
10421     
10422     fieldLabel : '',
10423     inputType : 'text',
10424     
10425     name : false,
10426     placeholder: false,
10427     before : false,
10428     after : false,
10429     size : false,
10430     hasFocus : false,
10431     preventMark: false,
10432     isFormField : true,
10433     value : '',
10434     labelWidth : 2,
10435     labelAlign : false,
10436     readOnly : false,
10437     align : false,
10438     formatedValue : false,
10439     forceFeedback : false,
10440     
10441     indicatorpos : 'left',
10442     
10443     labellg : 0,
10444     labelmd : 0,
10445     labelsm : 0,
10446     labelxs : 0,
10447     
10448     capture : '',
10449     accept : '',
10450     
10451     parentLabelAlign : function()
10452     {
10453         var parent = this;
10454         while (parent.parent()) {
10455             parent = parent.parent();
10456             if (typeof(parent.labelAlign) !='undefined') {
10457                 return parent.labelAlign;
10458             }
10459         }
10460         return 'left';
10461         
10462     },
10463     
10464     getAutoCreate : function()
10465     {
10466         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10467         
10468         var id = Roo.id();
10469         
10470         var cfg = {};
10471         
10472         if(this.inputType != 'hidden'){
10473             cfg.cls = 'form-group' //input-group
10474         }
10475         
10476         var input =  {
10477             tag: 'input',
10478             id : id,
10479             type : this.inputType,
10480             value : this.value,
10481             cls : 'form-control',
10482             placeholder : this.placeholder || '',
10483             autocomplete : this.autocomplete || 'new-password'
10484         };
10485         if (this.inputType == 'file') {
10486             input.style = 'overflow:hidden'; // why not in CSS?
10487         }
10488         
10489         if(this.capture.length){
10490             input.capture = this.capture;
10491         }
10492         
10493         if(this.accept.length){
10494             input.accept = this.accept + "/*";
10495         }
10496         
10497         if(this.align){
10498             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10499         }
10500         
10501         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10502             input.maxLength = this.maxLength;
10503         }
10504         
10505         if (this.disabled) {
10506             input.disabled=true;
10507         }
10508         
10509         if (this.readOnly) {
10510             input.readonly=true;
10511         }
10512         
10513         if (this.name) {
10514             input.name = this.name;
10515         }
10516         
10517         if (this.size) {
10518             input.cls += ' input-' + this.size;
10519         }
10520         
10521         var settings=this;
10522         ['xs','sm','md','lg'].map(function(size){
10523             if (settings[size]) {
10524                 cfg.cls += ' col-' + size + '-' + settings[size];
10525             }
10526         });
10527         
10528         var inputblock = input;
10529         
10530         var feedback = {
10531             tag: 'span',
10532             cls: 'glyphicon form-control-feedback'
10533         };
10534             
10535         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10536             
10537             inputblock = {
10538                 cls : 'has-feedback',
10539                 cn :  [
10540                     input,
10541                     feedback
10542                 ] 
10543             };  
10544         }
10545         
10546         if (this.before || this.after) {
10547             
10548             inputblock = {
10549                 cls : 'input-group',
10550                 cn :  [] 
10551             };
10552             
10553             if (this.before && typeof(this.before) == 'string') {
10554                 
10555                 inputblock.cn.push({
10556                     tag :'span',
10557                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10558                     html : this.before
10559                 });
10560             }
10561             if (this.before && typeof(this.before) == 'object') {
10562                 this.before = Roo.factory(this.before);
10563                 
10564                 inputblock.cn.push({
10565                     tag :'span',
10566                     cls : 'roo-input-before input-group-prepend   input-group-' +
10567                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10568                 });
10569             }
10570             
10571             inputblock.cn.push(input);
10572             
10573             if (this.after && typeof(this.after) == 'string') {
10574                 inputblock.cn.push({
10575                     tag :'span',
10576                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10577                     html : this.after
10578                 });
10579             }
10580             if (this.after && typeof(this.after) == 'object') {
10581                 this.after = Roo.factory(this.after);
10582                 
10583                 inputblock.cn.push({
10584                     tag :'span',
10585                     cls : 'roo-input-after input-group-append  input-group-' +
10586                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10587                 });
10588             }
10589             
10590             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10591                 inputblock.cls += ' has-feedback';
10592                 inputblock.cn.push(feedback);
10593             }
10594         };
10595         var indicator = {
10596             tag : 'i',
10597             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10598             tooltip : 'This field is required'
10599         };
10600         if (this.allowBlank ) {
10601             indicator.style = this.allowBlank ? ' display:none' : '';
10602         }
10603         if (align ==='left' && this.fieldLabel.length) {
10604             
10605             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10606             
10607             cfg.cn = [
10608                 indicator,
10609                 {
10610                     tag: 'label',
10611                     'for' :  id,
10612                     cls : 'control-label col-form-label',
10613                     html : this.fieldLabel
10614
10615                 },
10616                 {
10617                     cls : "", 
10618                     cn: [
10619                         inputblock
10620                     ]
10621                 }
10622             ];
10623             
10624             var labelCfg = cfg.cn[1];
10625             var contentCfg = cfg.cn[2];
10626             
10627             if(this.indicatorpos == 'right'){
10628                 cfg.cn = [
10629                     {
10630                         tag: 'label',
10631                         'for' :  id,
10632                         cls : 'control-label col-form-label',
10633                         cn : [
10634                             {
10635                                 tag : 'span',
10636                                 html : this.fieldLabel
10637                             },
10638                             indicator
10639                         ]
10640                     },
10641                     {
10642                         cls : "",
10643                         cn: [
10644                             inputblock
10645                         ]
10646                     }
10647
10648                 ];
10649                 
10650                 labelCfg = cfg.cn[0];
10651                 contentCfg = cfg.cn[1];
10652             
10653             }
10654             
10655             if(this.labelWidth > 12){
10656                 labelCfg.style = "width: " + this.labelWidth + 'px';
10657             }
10658             
10659             if(this.labelWidth < 13 && this.labelmd == 0){
10660                 this.labelmd = this.labelWidth;
10661             }
10662             
10663             if(this.labellg > 0){
10664                 labelCfg.cls += ' col-lg-' + this.labellg;
10665                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10666             }
10667             
10668             if(this.labelmd > 0){
10669                 labelCfg.cls += ' col-md-' + this.labelmd;
10670                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10671             }
10672             
10673             if(this.labelsm > 0){
10674                 labelCfg.cls += ' col-sm-' + this.labelsm;
10675                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10676             }
10677             
10678             if(this.labelxs > 0){
10679                 labelCfg.cls += ' col-xs-' + this.labelxs;
10680                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10681             }
10682             
10683             
10684         } else if ( this.fieldLabel.length) {
10685                 
10686             
10687             
10688             cfg.cn = [
10689                 {
10690                     tag : 'i',
10691                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10692                     tooltip : 'This field is required',
10693                     style : this.allowBlank ? ' display:none' : '' 
10694                 },
10695                 {
10696                     tag: 'label',
10697                    //cls : 'input-group-addon',
10698                     html : this.fieldLabel
10699
10700                 },
10701
10702                inputblock
10703
10704            ];
10705            
10706            if(this.indicatorpos == 'right'){
10707        
10708                 cfg.cn = [
10709                     {
10710                         tag: 'label',
10711                        //cls : 'input-group-addon',
10712                         html : this.fieldLabel
10713
10714                     },
10715                     {
10716                         tag : 'i',
10717                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10718                         tooltip : 'This field is required',
10719                         style : this.allowBlank ? ' display:none' : '' 
10720                     },
10721
10722                    inputblock
10723
10724                ];
10725
10726             }
10727
10728         } else {
10729             
10730             cfg.cn = [
10731
10732                     inputblock
10733
10734             ];
10735                 
10736                 
10737         };
10738         
10739         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10740            cfg.cls += ' navbar-form';
10741         }
10742         
10743         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10744             // on BS4 we do this only if not form 
10745             cfg.cls += ' navbar-form';
10746             cfg.tag = 'li';
10747         }
10748         
10749         return cfg;
10750         
10751     },
10752     /**
10753      * return the real input element.
10754      */
10755     inputEl: function ()
10756     {
10757         return this.el.select('input.form-control',true).first();
10758     },
10759     
10760     tooltipEl : function()
10761     {
10762         return this.inputEl();
10763     },
10764     
10765     indicatorEl : function()
10766     {
10767         if (Roo.bootstrap.version == 4) {
10768             return false; // not enabled in v4 yet.
10769         }
10770         
10771         var indicator = this.el.select('i.roo-required-indicator',true).first();
10772         
10773         if(!indicator){
10774             return false;
10775         }
10776         
10777         return indicator;
10778         
10779     },
10780     
10781     setDisabled : function(v)
10782     {
10783         var i  = this.inputEl().dom;
10784         if (!v) {
10785             i.removeAttribute('disabled');
10786             return;
10787             
10788         }
10789         i.setAttribute('disabled','true');
10790     },
10791     initEvents : function()
10792     {
10793           
10794         this.inputEl().on("keydown" , this.fireKey,  this);
10795         this.inputEl().on("focus", this.onFocus,  this);
10796         this.inputEl().on("blur", this.onBlur,  this);
10797         
10798         this.inputEl().relayEvent('keyup', this);
10799         
10800         this.indicator = this.indicatorEl();
10801         
10802         if(this.indicator){
10803             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10804         }
10805  
10806         // reference to original value for reset
10807         this.originalValue = this.getValue();
10808         //Roo.form.TextField.superclass.initEvents.call(this);
10809         if(this.validationEvent == 'keyup'){
10810             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10811             this.inputEl().on('keyup', this.filterValidation, this);
10812         }
10813         else if(this.validationEvent !== false){
10814             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10815         }
10816         
10817         if(this.selectOnFocus){
10818             this.on("focus", this.preFocus, this);
10819             
10820         }
10821         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10822             this.inputEl().on("keypress", this.filterKeys, this);
10823         } else {
10824             this.inputEl().relayEvent('keypress', this);
10825         }
10826        /* if(this.grow){
10827             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10828             this.el.on("click", this.autoSize,  this);
10829         }
10830         */
10831         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10832             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10833         }
10834         
10835         if (typeof(this.before) == 'object') {
10836             this.before.render(this.el.select('.roo-input-before',true).first());
10837         }
10838         if (typeof(this.after) == 'object') {
10839             this.after.render(this.el.select('.roo-input-after',true).first());
10840         }
10841         
10842         this.inputEl().on('change', this.onChange, this);
10843         
10844     },
10845     filterValidation : function(e){
10846         if(!e.isNavKeyPress()){
10847             this.validationTask.delay(this.validationDelay);
10848         }
10849     },
10850      /**
10851      * Validates the field value
10852      * @return {Boolean} True if the value is valid, else false
10853      */
10854     validate : function(){
10855         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10856         if(this.disabled || this.validateValue(this.getRawValue())){
10857             this.markValid();
10858             return true;
10859         }
10860         
10861         this.markInvalid();
10862         return false;
10863     },
10864     
10865     
10866     /**
10867      * Validates a value according to the field's validation rules and marks the field as invalid
10868      * if the validation fails
10869      * @param {Mixed} value The value to validate
10870      * @return {Boolean} True if the value is valid, else false
10871      */
10872     validateValue : function(value)
10873     {
10874         if(this.getVisibilityEl().hasClass('hidden')){
10875             return true;
10876         }
10877         
10878         if(value.length < 1)  { // if it's blank
10879             if(this.allowBlank){
10880                 return true;
10881             }
10882             return false;
10883         }
10884         
10885         if(value.length < this.minLength){
10886             return false;
10887         }
10888         if(value.length > this.maxLength){
10889             return false;
10890         }
10891         if(this.vtype){
10892             var vt = Roo.form.VTypes;
10893             if(!vt[this.vtype](value, this)){
10894                 return false;
10895             }
10896         }
10897         if(typeof this.validator == "function"){
10898             var msg = this.validator(value);
10899             if(msg !== true){
10900                 return false;
10901             }
10902             if (typeof(msg) == 'string') {
10903                 this.invalidText = msg;
10904             }
10905         }
10906         
10907         if(this.regex && !this.regex.test(value)){
10908             return false;
10909         }
10910         
10911         return true;
10912     },
10913     
10914      // private
10915     fireKey : function(e){
10916         //Roo.log('field ' + e.getKey());
10917         if(e.isNavKeyPress()){
10918             this.fireEvent("specialkey", this, e);
10919         }
10920     },
10921     focus : function (selectText){
10922         if(this.rendered){
10923             this.inputEl().focus();
10924             if(selectText === true){
10925                 this.inputEl().dom.select();
10926             }
10927         }
10928         return this;
10929     } ,
10930     
10931     onFocus : function(){
10932         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10933            // this.el.addClass(this.focusClass);
10934         }
10935         if(!this.hasFocus){
10936             this.hasFocus = true;
10937             this.startValue = this.getValue();
10938             this.fireEvent("focus", this);
10939         }
10940     },
10941     
10942     beforeBlur : Roo.emptyFn,
10943
10944     
10945     // private
10946     onBlur : function(){
10947         this.beforeBlur();
10948         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10949             //this.el.removeClass(this.focusClass);
10950         }
10951         this.hasFocus = false;
10952         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
10953             this.validate();
10954         }
10955         var v = this.getValue();
10956         if(String(v) !== String(this.startValue)){
10957             this.fireEvent('change', this, v, this.startValue);
10958         }
10959         this.fireEvent("blur", this);
10960     },
10961     
10962     onChange : function(e)
10963     {
10964         var v = this.getValue();
10965         if(String(v) !== String(this.startValue)){
10966             this.fireEvent('change', this, v, this.startValue);
10967         }
10968         
10969     },
10970     
10971     /**
10972      * Resets the current field value to the originally loaded value and clears any validation messages
10973      */
10974     reset : function(){
10975         this.setValue(this.originalValue);
10976         this.validate();
10977     },
10978      /**
10979      * Returns the name of the field
10980      * @return {Mixed} name The name field
10981      */
10982     getName: function(){
10983         return this.name;
10984     },
10985      /**
10986      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
10987      * @return {Mixed} value The field value
10988      */
10989     getValue : function(){
10990         
10991         var v = this.inputEl().getValue();
10992         
10993         return v;
10994     },
10995     /**
10996      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
10997      * @return {Mixed} value The field value
10998      */
10999     getRawValue : function(){
11000         var v = this.inputEl().getValue();
11001         
11002         return v;
11003     },
11004     
11005     /**
11006      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11007      * @param {Mixed} value The value to set
11008      */
11009     setRawValue : function(v){
11010         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11011     },
11012     
11013     selectText : function(start, end){
11014         var v = this.getRawValue();
11015         if(v.length > 0){
11016             start = start === undefined ? 0 : start;
11017             end = end === undefined ? v.length : end;
11018             var d = this.inputEl().dom;
11019             if(d.setSelectionRange){
11020                 d.setSelectionRange(start, end);
11021             }else if(d.createTextRange){
11022                 var range = d.createTextRange();
11023                 range.moveStart("character", start);
11024                 range.moveEnd("character", v.length-end);
11025                 range.select();
11026             }
11027         }
11028     },
11029     
11030     /**
11031      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11032      * @param {Mixed} value The value to set
11033      */
11034     setValue : function(v){
11035         this.value = v;
11036         if(this.rendered){
11037             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11038             this.validate();
11039         }
11040     },
11041     
11042     /*
11043     processValue : function(value){
11044         if(this.stripCharsRe){
11045             var newValue = value.replace(this.stripCharsRe, '');
11046             if(newValue !== value){
11047                 this.setRawValue(newValue);
11048                 return newValue;
11049             }
11050         }
11051         return value;
11052     },
11053   */
11054     preFocus : function(){
11055         
11056         if(this.selectOnFocus){
11057             this.inputEl().dom.select();
11058         }
11059     },
11060     filterKeys : function(e){
11061         var k = e.getKey();
11062         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11063             return;
11064         }
11065         var c = e.getCharCode(), cc = String.fromCharCode(c);
11066         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11067             return;
11068         }
11069         if(!this.maskRe.test(cc)){
11070             e.stopEvent();
11071         }
11072     },
11073      /**
11074      * Clear any invalid styles/messages for this field
11075      */
11076     clearInvalid : function(){
11077         
11078         if(!this.el || this.preventMark){ // not rendered
11079             return;
11080         }
11081         
11082         
11083         this.el.removeClass([this.invalidClass, 'is-invalid']);
11084         
11085         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11086             
11087             var feedback = this.el.select('.form-control-feedback', true).first();
11088             
11089             if(feedback){
11090                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11091             }
11092             
11093         }
11094         
11095         if(this.indicator){
11096             this.indicator.removeClass('visible');
11097             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11098         }
11099         
11100         this.fireEvent('valid', this);
11101     },
11102     
11103      /**
11104      * Mark this field as valid
11105      */
11106     markValid : function()
11107     {
11108         if(!this.el  || this.preventMark){ // not rendered...
11109             return;
11110         }
11111         
11112         this.el.removeClass([this.invalidClass, this.validClass]);
11113         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11114
11115         var feedback = this.el.select('.form-control-feedback', true).first();
11116             
11117         if(feedback){
11118             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11119         }
11120         
11121         if(this.indicator){
11122             this.indicator.removeClass('visible');
11123             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11124         }
11125         
11126         if(this.disabled){
11127             return;
11128         }
11129         
11130            
11131         if(this.allowBlank && !this.getRawValue().length){
11132             return;
11133         }
11134         if (Roo.bootstrap.version == 3) {
11135             this.el.addClass(this.validClass);
11136         } else {
11137             this.inputEl().addClass('is-valid');
11138         }
11139
11140         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11141             
11142             var feedback = this.el.select('.form-control-feedback', true).first();
11143             
11144             if(feedback){
11145                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11146                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11147             }
11148             
11149         }
11150         
11151         this.fireEvent('valid', this);
11152     },
11153     
11154      /**
11155      * Mark this field as invalid
11156      * @param {String} msg The validation message
11157      */
11158     markInvalid : function(msg)
11159     {
11160         if(!this.el  || this.preventMark){ // not rendered
11161             return;
11162         }
11163         
11164         this.el.removeClass([this.invalidClass, this.validClass]);
11165         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11166         
11167         var feedback = this.el.select('.form-control-feedback', true).first();
11168             
11169         if(feedback){
11170             this.el.select('.form-control-feedback', true).first().removeClass(
11171                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11172         }
11173
11174         if(this.disabled){
11175             return;
11176         }
11177         
11178         if(this.allowBlank && !this.getRawValue().length){
11179             return;
11180         }
11181         
11182         if(this.indicator){
11183             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11184             this.indicator.addClass('visible');
11185         }
11186         if (Roo.bootstrap.version == 3) {
11187             this.el.addClass(this.invalidClass);
11188         } else {
11189             this.inputEl().addClass('is-invalid');
11190         }
11191         
11192         
11193         
11194         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11195             
11196             var feedback = this.el.select('.form-control-feedback', true).first();
11197             
11198             if(feedback){
11199                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11200                 
11201                 if(this.getValue().length || this.forceFeedback){
11202                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11203                 }
11204                 
11205             }
11206             
11207         }
11208         
11209         this.fireEvent('invalid', this, msg);
11210     },
11211     // private
11212     SafariOnKeyDown : function(event)
11213     {
11214         // this is a workaround for a password hang bug on chrome/ webkit.
11215         if (this.inputEl().dom.type != 'password') {
11216             return;
11217         }
11218         
11219         var isSelectAll = false;
11220         
11221         if(this.inputEl().dom.selectionEnd > 0){
11222             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11223         }
11224         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11225             event.preventDefault();
11226             this.setValue('');
11227             return;
11228         }
11229         
11230         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11231             
11232             event.preventDefault();
11233             // this is very hacky as keydown always get's upper case.
11234             //
11235             var cc = String.fromCharCode(event.getCharCode());
11236             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11237             
11238         }
11239     },
11240     adjustWidth : function(tag, w){
11241         tag = tag.toLowerCase();
11242         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11243             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11244                 if(tag == 'input'){
11245                     return w + 2;
11246                 }
11247                 if(tag == 'textarea'){
11248                     return w-2;
11249                 }
11250             }else if(Roo.isOpera){
11251                 if(tag == 'input'){
11252                     return w + 2;
11253                 }
11254                 if(tag == 'textarea'){
11255                     return w-2;
11256                 }
11257             }
11258         }
11259         return w;
11260     },
11261     
11262     setFieldLabel : function(v)
11263     {
11264         if(!this.rendered){
11265             return;
11266         }
11267         
11268         if(this.indicatorEl()){
11269             var ar = this.el.select('label > span',true);
11270             
11271             if (ar.elements.length) {
11272                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11273                 this.fieldLabel = v;
11274                 return;
11275             }
11276             
11277             var br = this.el.select('label',true);
11278             
11279             if(br.elements.length) {
11280                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11281                 this.fieldLabel = v;
11282                 return;
11283             }
11284             
11285             Roo.log('Cannot Found any of label > span || label in input');
11286             return;
11287         }
11288         
11289         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11290         this.fieldLabel = v;
11291         
11292         
11293     }
11294 });
11295
11296  
11297 /*
11298  * - LGPL
11299  *
11300  * Input
11301  * 
11302  */
11303
11304 /**
11305  * @class Roo.bootstrap.TextArea
11306  * @extends Roo.bootstrap.Input
11307  * Bootstrap TextArea class
11308  * @cfg {Number} cols Specifies the visible width of a text area
11309  * @cfg {Number} rows Specifies the visible number of lines in a text area
11310  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11311  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11312  * @cfg {string} html text
11313  * 
11314  * @constructor
11315  * Create a new TextArea
11316  * @param {Object} config The config object
11317  */
11318
11319 Roo.bootstrap.TextArea = function(config){
11320     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11321    
11322 };
11323
11324 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11325      
11326     cols : false,
11327     rows : 5,
11328     readOnly : false,
11329     warp : 'soft',
11330     resize : false,
11331     value: false,
11332     html: false,
11333     
11334     getAutoCreate : function(){
11335         
11336         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11337         
11338         var id = Roo.id();
11339         
11340         var cfg = {};
11341         
11342         if(this.inputType != 'hidden'){
11343             cfg.cls = 'form-group' //input-group
11344         }
11345         
11346         var input =  {
11347             tag: 'textarea',
11348             id : id,
11349             warp : this.warp,
11350             rows : this.rows,
11351             value : this.value || '',
11352             html: this.html || '',
11353             cls : 'form-control',
11354             placeholder : this.placeholder || '' 
11355             
11356         };
11357         
11358         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11359             input.maxLength = this.maxLength;
11360         }
11361         
11362         if(this.resize){
11363             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11364         }
11365         
11366         if(this.cols){
11367             input.cols = this.cols;
11368         }
11369         
11370         if (this.readOnly) {
11371             input.readonly = true;
11372         }
11373         
11374         if (this.name) {
11375             input.name = this.name;
11376         }
11377         
11378         if (this.size) {
11379             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11380         }
11381         
11382         var settings=this;
11383         ['xs','sm','md','lg'].map(function(size){
11384             if (settings[size]) {
11385                 cfg.cls += ' col-' + size + '-' + settings[size];
11386             }
11387         });
11388         
11389         var inputblock = input;
11390         
11391         if(this.hasFeedback && !this.allowBlank){
11392             
11393             var feedback = {
11394                 tag: 'span',
11395                 cls: 'glyphicon form-control-feedback'
11396             };
11397
11398             inputblock = {
11399                 cls : 'has-feedback',
11400                 cn :  [
11401                     input,
11402                     feedback
11403                 ] 
11404             };  
11405         }
11406         
11407         
11408         if (this.before || this.after) {
11409             
11410             inputblock = {
11411                 cls : 'input-group',
11412                 cn :  [] 
11413             };
11414             if (this.before) {
11415                 inputblock.cn.push({
11416                     tag :'span',
11417                     cls : 'input-group-addon',
11418                     html : this.before
11419                 });
11420             }
11421             
11422             inputblock.cn.push(input);
11423             
11424             if(this.hasFeedback && !this.allowBlank){
11425                 inputblock.cls += ' has-feedback';
11426                 inputblock.cn.push(feedback);
11427             }
11428             
11429             if (this.after) {
11430                 inputblock.cn.push({
11431                     tag :'span',
11432                     cls : 'input-group-addon',
11433                     html : this.after
11434                 });
11435             }
11436             
11437         }
11438         
11439         if (align ==='left' && this.fieldLabel.length) {
11440             cfg.cn = [
11441                 {
11442                     tag: 'label',
11443                     'for' :  id,
11444                     cls : 'control-label',
11445                     html : this.fieldLabel
11446                 },
11447                 {
11448                     cls : "",
11449                     cn: [
11450                         inputblock
11451                     ]
11452                 }
11453
11454             ];
11455             
11456             if(this.labelWidth > 12){
11457                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11458             }
11459
11460             if(this.labelWidth < 13 && this.labelmd == 0){
11461                 this.labelmd = this.labelWidth;
11462             }
11463
11464             if(this.labellg > 0){
11465                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11466                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11467             }
11468
11469             if(this.labelmd > 0){
11470                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11471                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11472             }
11473
11474             if(this.labelsm > 0){
11475                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11476                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11477             }
11478
11479             if(this.labelxs > 0){
11480                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11481                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11482             }
11483             
11484         } else if ( this.fieldLabel.length) {
11485             cfg.cn = [
11486
11487                {
11488                    tag: 'label',
11489                    //cls : 'input-group-addon',
11490                    html : this.fieldLabel
11491
11492                },
11493
11494                inputblock
11495
11496            ];
11497
11498         } else {
11499
11500             cfg.cn = [
11501
11502                 inputblock
11503
11504             ];
11505                 
11506         }
11507         
11508         if (this.disabled) {
11509             input.disabled=true;
11510         }
11511         
11512         return cfg;
11513         
11514     },
11515     /**
11516      * return the real textarea element.
11517      */
11518     inputEl: function ()
11519     {
11520         return this.el.select('textarea.form-control',true).first();
11521     },
11522     
11523     /**
11524      * Clear any invalid styles/messages for this field
11525      */
11526     clearInvalid : function()
11527     {
11528         
11529         if(!this.el || this.preventMark){ // not rendered
11530             return;
11531         }
11532         
11533         var label = this.el.select('label', true).first();
11534         var icon = this.el.select('i.fa-star', true).first();
11535         
11536         if(label && icon){
11537             icon.remove();
11538         }
11539         this.el.removeClass( this.validClass);
11540         this.inputEl().removeClass('is-invalid');
11541          
11542         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11543             
11544             var feedback = this.el.select('.form-control-feedback', true).first();
11545             
11546             if(feedback){
11547                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11548             }
11549             
11550         }
11551         
11552         this.fireEvent('valid', this);
11553     },
11554     
11555      /**
11556      * Mark this field as valid
11557      */
11558     markValid : function()
11559     {
11560         if(!this.el  || this.preventMark){ // not rendered
11561             return;
11562         }
11563         
11564         this.el.removeClass([this.invalidClass, this.validClass]);
11565         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11566         
11567         var feedback = this.el.select('.form-control-feedback', true).first();
11568             
11569         if(feedback){
11570             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11571         }
11572
11573         if(this.disabled || this.allowBlank){
11574             return;
11575         }
11576         
11577         var label = this.el.select('label', true).first();
11578         var icon = this.el.select('i.fa-star', true).first();
11579         
11580         if(label && icon){
11581             icon.remove();
11582         }
11583         if (Roo.bootstrap.version == 3) {
11584             this.el.addClass(this.validClass);
11585         } else {
11586             this.inputEl().addClass('is-valid');
11587         }
11588         
11589         
11590         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11591             
11592             var feedback = this.el.select('.form-control-feedback', true).first();
11593             
11594             if(feedback){
11595                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11596                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11597             }
11598             
11599         }
11600         
11601         this.fireEvent('valid', this);
11602     },
11603     
11604      /**
11605      * Mark this field as invalid
11606      * @param {String} msg The validation message
11607      */
11608     markInvalid : function(msg)
11609     {
11610         if(!this.el  || this.preventMark){ // not rendered
11611             return;
11612         }
11613         
11614         this.el.removeClass([this.invalidClass, this.validClass]);
11615         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11616         
11617         var feedback = this.el.select('.form-control-feedback', true).first();
11618             
11619         if(feedback){
11620             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11621         }
11622
11623         if(this.disabled || this.allowBlank){
11624             return;
11625         }
11626         
11627         var label = this.el.select('label', true).first();
11628         var icon = this.el.select('i.fa-star', true).first();
11629         
11630         if(!this.getValue().length && label && !icon){
11631             this.el.createChild({
11632                 tag : 'i',
11633                 cls : 'text-danger fa fa-lg fa-star',
11634                 tooltip : 'This field is required',
11635                 style : 'margin-right:5px;'
11636             }, label, true);
11637         }
11638         
11639         if (Roo.bootstrap.version == 3) {
11640             this.el.addClass(this.invalidClass);
11641         } else {
11642             this.inputEl().addClass('is-invalid');
11643         }
11644         
11645         // fixme ... this may be depricated need to test..
11646         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11647             
11648             var feedback = this.el.select('.form-control-feedback', true).first();
11649             
11650             if(feedback){
11651                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11652                 
11653                 if(this.getValue().length || this.forceFeedback){
11654                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11655                 }
11656                 
11657             }
11658             
11659         }
11660         
11661         this.fireEvent('invalid', this, msg);
11662     }
11663 });
11664
11665  
11666 /*
11667  * - LGPL
11668  *
11669  * trigger field - base class for combo..
11670  * 
11671  */
11672  
11673 /**
11674  * @class Roo.bootstrap.TriggerField
11675  * @extends Roo.bootstrap.Input
11676  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11677  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11678  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11679  * for which you can provide a custom implementation.  For example:
11680  * <pre><code>
11681 var trigger = new Roo.bootstrap.TriggerField();
11682 trigger.onTriggerClick = myTriggerFn;
11683 trigger.applyTo('my-field');
11684 </code></pre>
11685  *
11686  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11687  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11688  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11689  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11690  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11691
11692  * @constructor
11693  * Create a new TriggerField.
11694  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11695  * to the base TextField)
11696  */
11697 Roo.bootstrap.TriggerField = function(config){
11698     this.mimicing = false;
11699     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11700 };
11701
11702 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11703     /**
11704      * @cfg {String} triggerClass A CSS class to apply to the trigger
11705      */
11706      /**
11707      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11708      */
11709     hideTrigger:false,
11710
11711     /**
11712      * @cfg {Boolean} removable (true|false) special filter default false
11713      */
11714     removable : false,
11715     
11716     /** @cfg {Boolean} grow @hide */
11717     /** @cfg {Number} growMin @hide */
11718     /** @cfg {Number} growMax @hide */
11719
11720     /**
11721      * @hide 
11722      * @method
11723      */
11724     autoSize: Roo.emptyFn,
11725     // private
11726     monitorTab : true,
11727     // private
11728     deferHeight : true,
11729
11730     
11731     actionMode : 'wrap',
11732     
11733     caret : false,
11734     
11735     
11736     getAutoCreate : function(){
11737        
11738         var align = this.labelAlign || this.parentLabelAlign();
11739         
11740         var id = Roo.id();
11741         
11742         var cfg = {
11743             cls: 'form-group' //input-group
11744         };
11745         
11746         
11747         var input =  {
11748             tag: 'input',
11749             id : id,
11750             type : this.inputType,
11751             cls : 'form-control',
11752             autocomplete: 'new-password',
11753             placeholder : this.placeholder || '' 
11754             
11755         };
11756         if (this.name) {
11757             input.name = this.name;
11758         }
11759         if (this.size) {
11760             input.cls += ' input-' + this.size;
11761         }
11762         
11763         if (this.disabled) {
11764             input.disabled=true;
11765         }
11766         
11767         var inputblock = input;
11768         
11769         if(this.hasFeedback && !this.allowBlank){
11770             
11771             var feedback = {
11772                 tag: 'span',
11773                 cls: 'glyphicon form-control-feedback'
11774             };
11775             
11776             if(this.removable && !this.editable  ){
11777                 inputblock = {
11778                     cls : 'has-feedback',
11779                     cn :  [
11780                         inputblock,
11781                         {
11782                             tag: 'button',
11783                             html : 'x',
11784                             cls : 'roo-combo-removable-btn close'
11785                         },
11786                         feedback
11787                     ] 
11788                 };
11789             } else {
11790                 inputblock = {
11791                     cls : 'has-feedback',
11792                     cn :  [
11793                         inputblock,
11794                         feedback
11795                     ] 
11796                 };
11797             }
11798
11799         } else {
11800             if(this.removable && !this.editable ){
11801                 inputblock = {
11802                     cls : 'roo-removable',
11803                     cn :  [
11804                         inputblock,
11805                         {
11806                             tag: 'button',
11807                             html : 'x',
11808                             cls : 'roo-combo-removable-btn close'
11809                         }
11810                     ] 
11811                 };
11812             }
11813         }
11814         
11815         if (this.before || this.after) {
11816             
11817             inputblock = {
11818                 cls : 'input-group',
11819                 cn :  [] 
11820             };
11821             if (this.before) {
11822                 inputblock.cn.push({
11823                     tag :'span',
11824                     cls : 'input-group-addon input-group-prepend input-group-text',
11825                     html : this.before
11826                 });
11827             }
11828             
11829             inputblock.cn.push(input);
11830             
11831             if(this.hasFeedback && !this.allowBlank){
11832                 inputblock.cls += ' has-feedback';
11833                 inputblock.cn.push(feedback);
11834             }
11835             
11836             if (this.after) {
11837                 inputblock.cn.push({
11838                     tag :'span',
11839                     cls : 'input-group-addon input-group-append input-group-text',
11840                     html : this.after
11841                 });
11842             }
11843             
11844         };
11845         
11846       
11847         
11848         var ibwrap = inputblock;
11849         
11850         if(this.multiple){
11851             ibwrap = {
11852                 tag: 'ul',
11853                 cls: 'roo-select2-choices',
11854                 cn:[
11855                     {
11856                         tag: 'li',
11857                         cls: 'roo-select2-search-field',
11858                         cn: [
11859
11860                             inputblock
11861                         ]
11862                     }
11863                 ]
11864             };
11865                 
11866         }
11867         
11868         var combobox = {
11869             cls: 'roo-select2-container input-group',
11870             cn: [
11871                  {
11872                     tag: 'input',
11873                     type : 'hidden',
11874                     cls: 'form-hidden-field'
11875                 },
11876                 ibwrap
11877             ]
11878         };
11879         
11880         if(!this.multiple && this.showToggleBtn){
11881             
11882             var caret = {
11883                         tag: 'span',
11884                         cls: 'caret'
11885              };
11886             if (this.caret != false) {
11887                 caret = {
11888                      tag: 'i',
11889                      cls: 'fa fa-' + this.caret
11890                 };
11891                 
11892             }
11893             
11894             combobox.cn.push({
11895                 tag :'span',
11896                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11897                 cn : [
11898                     Roo.bootstrap.version == 3 ? caret : '',
11899                     {
11900                         tag: 'span',
11901                         cls: 'combobox-clear',
11902                         cn  : [
11903                             {
11904                                 tag : 'i',
11905                                 cls: 'icon-remove'
11906                             }
11907                         ]
11908                     }
11909                 ]
11910
11911             })
11912         }
11913         
11914         if(this.multiple){
11915             combobox.cls += ' roo-select2-container-multi';
11916         }
11917          var indicator = {
11918             tag : 'i',
11919             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11920             tooltip : 'This field is required'
11921         };
11922         if (Roo.bootstrap.version == 4) {
11923             indicator = {
11924                 tag : 'i',
11925                 style : 'display:none'
11926             };
11927         }
11928         
11929         
11930         if (align ==='left' && this.fieldLabel.length) {
11931             
11932             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11933
11934             cfg.cn = [
11935                 indicator,
11936                 {
11937                     tag: 'label',
11938                     'for' :  id,
11939                     cls : 'control-label',
11940                     html : this.fieldLabel
11941
11942                 },
11943                 {
11944                     cls : "", 
11945                     cn: [
11946                         combobox
11947                     ]
11948                 }
11949
11950             ];
11951             
11952             var labelCfg = cfg.cn[1];
11953             var contentCfg = cfg.cn[2];
11954             
11955             if(this.indicatorpos == 'right'){
11956                 cfg.cn = [
11957                     {
11958                         tag: 'label',
11959                         'for' :  id,
11960                         cls : 'control-label',
11961                         cn : [
11962                             {
11963                                 tag : 'span',
11964                                 html : this.fieldLabel
11965                             },
11966                             indicator
11967                         ]
11968                     },
11969                     {
11970                         cls : "", 
11971                         cn: [
11972                             combobox
11973                         ]
11974                     }
11975
11976                 ];
11977                 
11978                 labelCfg = cfg.cn[0];
11979                 contentCfg = cfg.cn[1];
11980             }
11981             
11982             if(this.labelWidth > 12){
11983                 labelCfg.style = "width: " + this.labelWidth + 'px';
11984             }
11985             
11986             if(this.labelWidth < 13 && this.labelmd == 0){
11987                 this.labelmd = this.labelWidth;
11988             }
11989             
11990             if(this.labellg > 0){
11991                 labelCfg.cls += ' col-lg-' + this.labellg;
11992                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11993             }
11994             
11995             if(this.labelmd > 0){
11996                 labelCfg.cls += ' col-md-' + this.labelmd;
11997                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11998             }
11999             
12000             if(this.labelsm > 0){
12001                 labelCfg.cls += ' col-sm-' + this.labelsm;
12002                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12003             }
12004             
12005             if(this.labelxs > 0){
12006                 labelCfg.cls += ' col-xs-' + this.labelxs;
12007                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12008             }
12009             
12010         } else if ( this.fieldLabel.length) {
12011 //                Roo.log(" label");
12012             cfg.cn = [
12013                 indicator,
12014                {
12015                    tag: 'label',
12016                    //cls : 'input-group-addon',
12017                    html : this.fieldLabel
12018
12019                },
12020
12021                combobox
12022
12023             ];
12024             
12025             if(this.indicatorpos == 'right'){
12026                 
12027                 cfg.cn = [
12028                     {
12029                        tag: 'label',
12030                        cn : [
12031                            {
12032                                tag : 'span',
12033                                html : this.fieldLabel
12034                            },
12035                            indicator
12036                        ]
12037
12038                     },
12039                     combobox
12040
12041                 ];
12042
12043             }
12044
12045         } else {
12046             
12047 //                Roo.log(" no label && no align");
12048                 cfg = combobox
12049                      
12050                 
12051         }
12052         
12053         var settings=this;
12054         ['xs','sm','md','lg'].map(function(size){
12055             if (settings[size]) {
12056                 cfg.cls += ' col-' + size + '-' + settings[size];
12057             }
12058         });
12059         
12060         return cfg;
12061         
12062     },
12063     
12064     
12065     
12066     // private
12067     onResize : function(w, h){
12068 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12069 //        if(typeof w == 'number'){
12070 //            var x = w - this.trigger.getWidth();
12071 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12072 //            this.trigger.setStyle('left', x+'px');
12073 //        }
12074     },
12075
12076     // private
12077     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12078
12079     // private
12080     getResizeEl : function(){
12081         return this.inputEl();
12082     },
12083
12084     // private
12085     getPositionEl : function(){
12086         return this.inputEl();
12087     },
12088
12089     // private
12090     alignErrorIcon : function(){
12091         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12092     },
12093
12094     // private
12095     initEvents : function(){
12096         
12097         this.createList();
12098         
12099         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12100         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12101         if(!this.multiple && this.showToggleBtn){
12102             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12103             if(this.hideTrigger){
12104                 this.trigger.setDisplayed(false);
12105             }
12106             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12107         }
12108         
12109         if(this.multiple){
12110             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12111         }
12112         
12113         if(this.removable && !this.editable && !this.tickable){
12114             var close = this.closeTriggerEl();
12115             
12116             if(close){
12117                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12118                 close.on('click', this.removeBtnClick, this, close);
12119             }
12120         }
12121         
12122         //this.trigger.addClassOnOver('x-form-trigger-over');
12123         //this.trigger.addClassOnClick('x-form-trigger-click');
12124         
12125         //if(!this.width){
12126         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12127         //}
12128     },
12129     
12130     closeTriggerEl : function()
12131     {
12132         var close = this.el.select('.roo-combo-removable-btn', true).first();
12133         return close ? close : false;
12134     },
12135     
12136     removeBtnClick : function(e, h, el)
12137     {
12138         e.preventDefault();
12139         
12140         if(this.fireEvent("remove", this) !== false){
12141             this.reset();
12142             this.fireEvent("afterremove", this)
12143         }
12144     },
12145     
12146     createList : function()
12147     {
12148         this.list = Roo.get(document.body).createChild({
12149             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12150             cls: 'typeahead typeahead-long dropdown-menu',
12151             style: 'display:none'
12152         });
12153         
12154         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12155         
12156     },
12157
12158     // private
12159     initTrigger : function(){
12160        
12161     },
12162
12163     // private
12164     onDestroy : function(){
12165         if(this.trigger){
12166             this.trigger.removeAllListeners();
12167           //  this.trigger.remove();
12168         }
12169         //if(this.wrap){
12170         //    this.wrap.remove();
12171         //}
12172         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12173     },
12174
12175     // private
12176     onFocus : function(){
12177         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12178         /*
12179         if(!this.mimicing){
12180             this.wrap.addClass('x-trigger-wrap-focus');
12181             this.mimicing = true;
12182             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12183             if(this.monitorTab){
12184                 this.el.on("keydown", this.checkTab, this);
12185             }
12186         }
12187         */
12188     },
12189
12190     // private
12191     checkTab : function(e){
12192         if(e.getKey() == e.TAB){
12193             this.triggerBlur();
12194         }
12195     },
12196
12197     // private
12198     onBlur : function(){
12199         // do nothing
12200     },
12201
12202     // private
12203     mimicBlur : function(e, t){
12204         /*
12205         if(!this.wrap.contains(t) && this.validateBlur()){
12206             this.triggerBlur();
12207         }
12208         */
12209     },
12210
12211     // private
12212     triggerBlur : function(){
12213         this.mimicing = false;
12214         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12215         if(this.monitorTab){
12216             this.el.un("keydown", this.checkTab, this);
12217         }
12218         //this.wrap.removeClass('x-trigger-wrap-focus');
12219         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12220     },
12221
12222     // private
12223     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12224     validateBlur : function(e, t){
12225         return true;
12226     },
12227
12228     // private
12229     onDisable : function(){
12230         this.inputEl().dom.disabled = true;
12231         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12232         //if(this.wrap){
12233         //    this.wrap.addClass('x-item-disabled');
12234         //}
12235     },
12236
12237     // private
12238     onEnable : function(){
12239         this.inputEl().dom.disabled = false;
12240         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12241         //if(this.wrap){
12242         //    this.el.removeClass('x-item-disabled');
12243         //}
12244     },
12245
12246     // private
12247     onShow : function(){
12248         var ae = this.getActionEl();
12249         
12250         if(ae){
12251             ae.dom.style.display = '';
12252             ae.dom.style.visibility = 'visible';
12253         }
12254     },
12255
12256     // private
12257     
12258     onHide : function(){
12259         var ae = this.getActionEl();
12260         ae.dom.style.display = 'none';
12261     },
12262
12263     /**
12264      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12265      * by an implementing function.
12266      * @method
12267      * @param {EventObject} e
12268      */
12269     onTriggerClick : Roo.emptyFn
12270 });
12271  
12272 /*
12273 * Licence: LGPL
12274 */
12275
12276 /**
12277  * @class Roo.bootstrap.CardUploader
12278  * @extends Roo.bootstrap.Button
12279  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12280  * @cfg {Number} errorTimeout default 3000
12281  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12282  * @cfg {Array}  html The button text.
12283
12284  *
12285  * @constructor
12286  * Create a new CardUploader
12287  * @param {Object} config The config object
12288  */
12289
12290 Roo.bootstrap.CardUploader = function(config){
12291     
12292  
12293     
12294     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12295     
12296     
12297     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12298         return r.data.id
12299         });
12300     
12301     
12302 };
12303
12304 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12305     
12306      
12307     errorTimeout : 3000,
12308      
12309     images : false,
12310    
12311     fileCollection : false,
12312     allowBlank : true,
12313     
12314     getAutoCreate : function()
12315     {
12316         
12317         var cfg =  {
12318             cls :'form-group' ,
12319             cn : [
12320                
12321                 {
12322                     tag: 'label',
12323                    //cls : 'input-group-addon',
12324                     html : this.fieldLabel
12325
12326                 },
12327
12328                 {
12329                     tag: 'input',
12330                     type : 'hidden',
12331                     value : this.value,
12332                     cls : 'd-none  form-control'
12333                 },
12334                 
12335                 {
12336                     tag: 'input',
12337                     multiple : 'multiple',
12338                     type : 'file',
12339                     cls : 'd-none  roo-card-upload-selector'
12340                 },
12341                 
12342                 {
12343                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12344                 },
12345                 {
12346                     cls : 'card-columns roo-card-uploader-container'
12347                 }
12348
12349             ]
12350         };
12351            
12352          
12353         return cfg;
12354     },
12355     
12356     getChildContainer : function() /// what children are added to.
12357     {
12358         return this.containerEl;
12359     },
12360    
12361     getButtonContainer : function() /// what children are added to.
12362     {
12363         return this.el.select(".roo-card-uploader-button-container").first();
12364     },
12365    
12366     initEvents : function()
12367     {
12368         
12369         Roo.bootstrap.Input.prototype.initEvents.call(this);
12370         
12371         var t = this;
12372         this.addxtype({
12373             xns: Roo.bootstrap,
12374
12375             xtype : 'Button',
12376             container_method : 'getButtonContainer' ,            
12377             html :  this.html, // fix changable?
12378             cls : 'w-100 ',
12379             listeners : {
12380                 'click' : function(btn, e) {
12381                     t.onClick(e);
12382                 }
12383             }
12384         });
12385         
12386         
12387         
12388         
12389         this.urlAPI = (window.createObjectURL && window) || 
12390                                 (window.URL && URL.revokeObjectURL && URL) || 
12391                                 (window.webkitURL && webkitURL);
12392                         
12393          
12394          
12395          
12396         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12397         
12398         this.selectorEl.on('change', this.onFileSelected, this);
12399         if (this.images) {
12400             var t = this;
12401             this.images.forEach(function(img) {
12402                 t.addCard(img)
12403             });
12404             this.images = false;
12405         }
12406         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12407          
12408        
12409     },
12410     
12411    
12412     onClick : function(e)
12413     {
12414         e.preventDefault();
12415          
12416         this.selectorEl.dom.click();
12417          
12418     },
12419     
12420     onFileSelected : function(e)
12421     {
12422         e.preventDefault();
12423         
12424         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12425             return;
12426         }
12427         
12428         Roo.each(this.selectorEl.dom.files, function(file){    
12429             this.addFile(file);
12430         }, this);
12431          
12432     },
12433     
12434       
12435     
12436       
12437     
12438     addFile : function(file)
12439     {
12440            
12441         if(typeof(file) === 'string'){
12442             throw "Add file by name?"; // should not happen
12443             return;
12444         }
12445         
12446         if(!file || !this.urlAPI){
12447             return;
12448         }
12449         
12450         // file;
12451         // file.type;
12452         
12453         var _this = this;
12454         
12455         
12456         var url = _this.urlAPI.createObjectURL( file);
12457            
12458         this.addCard({
12459             id : Roo.bootstrap.CardUploader.ID--,
12460             is_uploaded : false,
12461             src : url,
12462             title : file.name,
12463             mimetype : file.type,
12464             preview : false,
12465             is_deleted : 0
12466         })
12467         
12468     },
12469     
12470     addCard : function (data)
12471     {
12472         // hidden input element?
12473         // if the file is not an image...
12474         //then we need to use something other that and header_image
12475         var t = this;
12476         //   remove.....
12477         var footer = [
12478             {
12479                 xns : Roo.bootstrap,
12480                 xtype : 'CardFooter',
12481                 items: [
12482                     {
12483                         xns : Roo.bootstrap,
12484                         xtype : 'Element',
12485                         cls : 'd-flex',
12486                         items : [
12487                             
12488                             {
12489                                 xns : Roo.bootstrap,
12490                                 xtype : 'Button',
12491                                 html : String.format("<small>{0}</small>", data.title),
12492                                 cls : 'col-11 text-left',
12493                                 size: 'sm',
12494                                 weight: 'link',
12495                                 fa : 'download',
12496                                 listeners : {
12497                                     click : function() {
12498                                         this.downloadCard(data.id)
12499                                     }
12500                                 }
12501                             },
12502                           
12503                             {
12504                                 xns : Roo.bootstrap,
12505                                 xtype : 'Button',
12506                                 
12507                                 size : 'sm',
12508                                 weight: 'danger',
12509                                 cls : 'col-1',
12510                                 fa : 'times',
12511                                 listeners : {
12512                                     click : function() {
12513                                         t.removeCard(data.id)
12514                                     }
12515                                 }
12516                             }
12517                         ]
12518                     }
12519                     
12520                 ] 
12521             }
12522             
12523         ];
12524
12525         var cn = this.addxtype(
12526             {
12527                  
12528                 xns : Roo.bootstrap,
12529                 xtype : 'Card',
12530                 closeable : true,
12531                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12532                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12533                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12534                 data : data,
12535                 html : false,
12536                  
12537                 items : footer,
12538                 initEvents : function() {
12539                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12540                     this.imgEl = this.el.select('.card-img-top').first();
12541                     if (this.imgEl) {
12542                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12543                         this.imgEl.set({ 'pointer' : 'cursor' });
12544                                   
12545                     }
12546                     
12547                   
12548                 }
12549                 
12550             }
12551         );
12552         // dont' really need ot update items.
12553         // this.items.push(cn);
12554         this.fileCollection.add(cn);
12555         this.updateInput();
12556         
12557     },
12558     removeCard : function(id)
12559     {
12560         
12561         var card  = this.fileCollection.get(id);
12562         card.data.is_deleted = 1;
12563         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12564         this.fileCollection.remove(card);
12565         //this.items = this.items.filter(function(e) { return e != card });
12566         // dont' really need ot update items.
12567         card.el.dom.parentNode.removeChild(card.el.dom);
12568         
12569     },
12570     reset: function()
12571     {
12572         this.fileCollection.each(function(card) {
12573             card.el.dom.parentNode.removeChild(card.el.dom);    
12574         });
12575         this.fileCollection.clear();
12576         this.updateInput();
12577     },
12578     
12579     updateInput : function()
12580     {
12581         var data = [];
12582         this.fileCollection.each(function(e) {
12583             data.push(e.data);
12584         });
12585         
12586         this.inputEl().dom.value = JSON.stringify(data);
12587     }
12588     
12589     
12590 });
12591
12592
12593 Roo.bootstrap.CardUploader.ID = -1;/*
12594  * Based on:
12595  * Ext JS Library 1.1.1
12596  * Copyright(c) 2006-2007, Ext JS, LLC.
12597  *
12598  * Originally Released Under LGPL - original licence link has changed is not relivant.
12599  *
12600  * Fork - LGPL
12601  * <script type="text/javascript">
12602  */
12603
12604
12605 /**
12606  * @class Roo.data.SortTypes
12607  * @singleton
12608  * Defines the default sorting (casting?) comparison functions used when sorting data.
12609  */
12610 Roo.data.SortTypes = {
12611     /**
12612      * Default sort that does nothing
12613      * @param {Mixed} s The value being converted
12614      * @return {Mixed} The comparison value
12615      */
12616     none : function(s){
12617         return s;
12618     },
12619     
12620     /**
12621      * The regular expression used to strip tags
12622      * @type {RegExp}
12623      * @property
12624      */
12625     stripTagsRE : /<\/?[^>]+>/gi,
12626     
12627     /**
12628      * Strips all HTML tags to sort on text only
12629      * @param {Mixed} s The value being converted
12630      * @return {String} The comparison value
12631      */
12632     asText : function(s){
12633         return String(s).replace(this.stripTagsRE, "");
12634     },
12635     
12636     /**
12637      * Strips all HTML tags to sort on text only - Case insensitive
12638      * @param {Mixed} s The value being converted
12639      * @return {String} The comparison value
12640      */
12641     asUCText : function(s){
12642         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12643     },
12644     
12645     /**
12646      * Case insensitive string
12647      * @param {Mixed} s The value being converted
12648      * @return {String} The comparison value
12649      */
12650     asUCString : function(s) {
12651         return String(s).toUpperCase();
12652     },
12653     
12654     /**
12655      * Date sorting
12656      * @param {Mixed} s The value being converted
12657      * @return {Number} The comparison value
12658      */
12659     asDate : function(s) {
12660         if(!s){
12661             return 0;
12662         }
12663         if(s instanceof Date){
12664             return s.getTime();
12665         }
12666         return Date.parse(String(s));
12667     },
12668     
12669     /**
12670      * Float sorting
12671      * @param {Mixed} s The value being converted
12672      * @return {Float} The comparison value
12673      */
12674     asFloat : function(s) {
12675         var val = parseFloat(String(s).replace(/,/g, ""));
12676         if(isNaN(val)) {
12677             val = 0;
12678         }
12679         return val;
12680     },
12681     
12682     /**
12683      * Integer sorting
12684      * @param {Mixed} s The value being converted
12685      * @return {Number} The comparison value
12686      */
12687     asInt : function(s) {
12688         var val = parseInt(String(s).replace(/,/g, ""));
12689         if(isNaN(val)) {
12690             val = 0;
12691         }
12692         return val;
12693     }
12694 };/*
12695  * Based on:
12696  * Ext JS Library 1.1.1
12697  * Copyright(c) 2006-2007, Ext JS, LLC.
12698  *
12699  * Originally Released Under LGPL - original licence link has changed is not relivant.
12700  *
12701  * Fork - LGPL
12702  * <script type="text/javascript">
12703  */
12704
12705 /**
12706 * @class Roo.data.Record
12707  * Instances of this class encapsulate both record <em>definition</em> information, and record
12708  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12709  * to access Records cached in an {@link Roo.data.Store} object.<br>
12710  * <p>
12711  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12712  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12713  * objects.<br>
12714  * <p>
12715  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12716  * @constructor
12717  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12718  * {@link #create}. The parameters are the same.
12719  * @param {Array} data An associative Array of data values keyed by the field name.
12720  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12721  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12722  * not specified an integer id is generated.
12723  */
12724 Roo.data.Record = function(data, id){
12725     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12726     this.data = data;
12727 };
12728
12729 /**
12730  * Generate a constructor for a specific record layout.
12731  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12732  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12733  * Each field definition object may contain the following properties: <ul>
12734  * <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,
12735  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12736  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12737  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12738  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12739  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12740  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12741  * this may be omitted.</p></li>
12742  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12743  * <ul><li>auto (Default, implies no conversion)</li>
12744  * <li>string</li>
12745  * <li>int</li>
12746  * <li>float</li>
12747  * <li>boolean</li>
12748  * <li>date</li></ul></p></li>
12749  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12750  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12751  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12752  * by the Reader into an object that will be stored in the Record. It is passed the
12753  * following parameters:<ul>
12754  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12755  * </ul></p></li>
12756  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12757  * </ul>
12758  * <br>usage:<br><pre><code>
12759 var TopicRecord = Roo.data.Record.create(
12760     {name: 'title', mapping: 'topic_title'},
12761     {name: 'author', mapping: 'username'},
12762     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12763     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12764     {name: 'lastPoster', mapping: 'user2'},
12765     {name: 'excerpt', mapping: 'post_text'}
12766 );
12767
12768 var myNewRecord = new TopicRecord({
12769     title: 'Do my job please',
12770     author: 'noobie',
12771     totalPosts: 1,
12772     lastPost: new Date(),
12773     lastPoster: 'Animal',
12774     excerpt: 'No way dude!'
12775 });
12776 myStore.add(myNewRecord);
12777 </code></pre>
12778  * @method create
12779  * @static
12780  */
12781 Roo.data.Record.create = function(o){
12782     var f = function(){
12783         f.superclass.constructor.apply(this, arguments);
12784     };
12785     Roo.extend(f, Roo.data.Record);
12786     var p = f.prototype;
12787     p.fields = new Roo.util.MixedCollection(false, function(field){
12788         return field.name;
12789     });
12790     for(var i = 0, len = o.length; i < len; i++){
12791         p.fields.add(new Roo.data.Field(o[i]));
12792     }
12793     f.getField = function(name){
12794         return p.fields.get(name);  
12795     };
12796     return f;
12797 };
12798
12799 Roo.data.Record.AUTO_ID = 1000;
12800 Roo.data.Record.EDIT = 'edit';
12801 Roo.data.Record.REJECT = 'reject';
12802 Roo.data.Record.COMMIT = 'commit';
12803
12804 Roo.data.Record.prototype = {
12805     /**
12806      * Readonly flag - true if this record has been modified.
12807      * @type Boolean
12808      */
12809     dirty : false,
12810     editing : false,
12811     error: null,
12812     modified: null,
12813
12814     // private
12815     join : function(store){
12816         this.store = store;
12817     },
12818
12819     /**
12820      * Set the named field to the specified value.
12821      * @param {String} name The name of the field to set.
12822      * @param {Object} value The value to set the field to.
12823      */
12824     set : function(name, value){
12825         if(this.data[name] == value){
12826             return;
12827         }
12828         this.dirty = true;
12829         if(!this.modified){
12830             this.modified = {};
12831         }
12832         if(typeof this.modified[name] == 'undefined'){
12833             this.modified[name] = this.data[name];
12834         }
12835         this.data[name] = value;
12836         if(!this.editing && this.store){
12837             this.store.afterEdit(this);
12838         }       
12839     },
12840
12841     /**
12842      * Get the value of the named field.
12843      * @param {String} name The name of the field to get the value of.
12844      * @return {Object} The value of the field.
12845      */
12846     get : function(name){
12847         return this.data[name]; 
12848     },
12849
12850     // private
12851     beginEdit : function(){
12852         this.editing = true;
12853         this.modified = {}; 
12854     },
12855
12856     // private
12857     cancelEdit : function(){
12858         this.editing = false;
12859         delete this.modified;
12860     },
12861
12862     // private
12863     endEdit : function(){
12864         this.editing = false;
12865         if(this.dirty && this.store){
12866             this.store.afterEdit(this);
12867         }
12868     },
12869
12870     /**
12871      * Usually called by the {@link Roo.data.Store} which owns the Record.
12872      * Rejects all changes made to the Record since either creation, or the last commit operation.
12873      * Modified fields are reverted to their original values.
12874      * <p>
12875      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12876      * of reject operations.
12877      */
12878     reject : function(){
12879         var m = this.modified;
12880         for(var n in m){
12881             if(typeof m[n] != "function"){
12882                 this.data[n] = m[n];
12883             }
12884         }
12885         this.dirty = false;
12886         delete this.modified;
12887         this.editing = false;
12888         if(this.store){
12889             this.store.afterReject(this);
12890         }
12891     },
12892
12893     /**
12894      * Usually called by the {@link Roo.data.Store} which owns the Record.
12895      * Commits all changes made to the Record since either creation, or the last commit operation.
12896      * <p>
12897      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12898      * of commit operations.
12899      */
12900     commit : function(){
12901         this.dirty = false;
12902         delete this.modified;
12903         this.editing = false;
12904         if(this.store){
12905             this.store.afterCommit(this);
12906         }
12907     },
12908
12909     // private
12910     hasError : function(){
12911         return this.error != null;
12912     },
12913
12914     // private
12915     clearError : function(){
12916         this.error = null;
12917     },
12918
12919     /**
12920      * Creates a copy of this record.
12921      * @param {String} id (optional) A new record id if you don't want to use this record's id
12922      * @return {Record}
12923      */
12924     copy : function(newId) {
12925         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12926     }
12927 };/*
12928  * Based on:
12929  * Ext JS Library 1.1.1
12930  * Copyright(c) 2006-2007, Ext JS, LLC.
12931  *
12932  * Originally Released Under LGPL - original licence link has changed is not relivant.
12933  *
12934  * Fork - LGPL
12935  * <script type="text/javascript">
12936  */
12937
12938
12939
12940 /**
12941  * @class Roo.data.Store
12942  * @extends Roo.util.Observable
12943  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12944  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12945  * <p>
12946  * 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
12947  * has no knowledge of the format of the data returned by the Proxy.<br>
12948  * <p>
12949  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12950  * instances from the data object. These records are cached and made available through accessor functions.
12951  * @constructor
12952  * Creates a new Store.
12953  * @param {Object} config A config object containing the objects needed for the Store to access data,
12954  * and read the data into Records.
12955  */
12956 Roo.data.Store = function(config){
12957     this.data = new Roo.util.MixedCollection(false);
12958     this.data.getKey = function(o){
12959         return o.id;
12960     };
12961     this.baseParams = {};
12962     // private
12963     this.paramNames = {
12964         "start" : "start",
12965         "limit" : "limit",
12966         "sort" : "sort",
12967         "dir" : "dir",
12968         "multisort" : "_multisort"
12969     };
12970
12971     if(config && config.data){
12972         this.inlineData = config.data;
12973         delete config.data;
12974     }
12975
12976     Roo.apply(this, config);
12977     
12978     if(this.reader){ // reader passed
12979         this.reader = Roo.factory(this.reader, Roo.data);
12980         this.reader.xmodule = this.xmodule || false;
12981         if(!this.recordType){
12982             this.recordType = this.reader.recordType;
12983         }
12984         if(this.reader.onMetaChange){
12985             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
12986         }
12987     }
12988
12989     if(this.recordType){
12990         this.fields = this.recordType.prototype.fields;
12991     }
12992     this.modified = [];
12993
12994     this.addEvents({
12995         /**
12996          * @event datachanged
12997          * Fires when the data cache has changed, and a widget which is using this Store
12998          * as a Record cache should refresh its view.
12999          * @param {Store} this
13000          */
13001         datachanged : true,
13002         /**
13003          * @event metachange
13004          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13005          * @param {Store} this
13006          * @param {Object} meta The JSON metadata
13007          */
13008         metachange : true,
13009         /**
13010          * @event add
13011          * Fires when Records have been added to the Store
13012          * @param {Store} this
13013          * @param {Roo.data.Record[]} records The array of Records added
13014          * @param {Number} index The index at which the record(s) were added
13015          */
13016         add : true,
13017         /**
13018          * @event remove
13019          * Fires when a Record has been removed from the Store
13020          * @param {Store} this
13021          * @param {Roo.data.Record} record The Record that was removed
13022          * @param {Number} index The index at which the record was removed
13023          */
13024         remove : true,
13025         /**
13026          * @event update
13027          * Fires when a Record has been updated
13028          * @param {Store} this
13029          * @param {Roo.data.Record} record The Record that was updated
13030          * @param {String} operation The update operation being performed.  Value may be one of:
13031          * <pre><code>
13032  Roo.data.Record.EDIT
13033  Roo.data.Record.REJECT
13034  Roo.data.Record.COMMIT
13035          * </code></pre>
13036          */
13037         update : true,
13038         /**
13039          * @event clear
13040          * Fires when the data cache has been cleared.
13041          * @param {Store} this
13042          */
13043         clear : true,
13044         /**
13045          * @event beforeload
13046          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13047          * the load action will be canceled.
13048          * @param {Store} this
13049          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13050          */
13051         beforeload : true,
13052         /**
13053          * @event beforeloadadd
13054          * Fires after a new set of Records has been loaded.
13055          * @param {Store} this
13056          * @param {Roo.data.Record[]} records The Records that were loaded
13057          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13058          */
13059         beforeloadadd : true,
13060         /**
13061          * @event load
13062          * Fires after a new set of Records has been loaded, before they are added to the store.
13063          * @param {Store} this
13064          * @param {Roo.data.Record[]} records The Records that were loaded
13065          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13066          * @params {Object} return from reader
13067          */
13068         load : true,
13069         /**
13070          * @event loadexception
13071          * Fires if an exception occurs in the Proxy during loading.
13072          * Called with the signature of the Proxy's "loadexception" event.
13073          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13074          * 
13075          * @param {Proxy} 
13076          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13077          * @param {Object} load options 
13078          * @param {Object} jsonData from your request (normally this contains the Exception)
13079          */
13080         loadexception : true
13081     });
13082     
13083     if(this.proxy){
13084         this.proxy = Roo.factory(this.proxy, Roo.data);
13085         this.proxy.xmodule = this.xmodule || false;
13086         this.relayEvents(this.proxy,  ["loadexception"]);
13087     }
13088     this.sortToggle = {};
13089     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13090
13091     Roo.data.Store.superclass.constructor.call(this);
13092
13093     if(this.inlineData){
13094         this.loadData(this.inlineData);
13095         delete this.inlineData;
13096     }
13097 };
13098
13099 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13100      /**
13101     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13102     * without a remote query - used by combo/forms at present.
13103     */
13104     
13105     /**
13106     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13107     */
13108     /**
13109     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13110     */
13111     /**
13112     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13113     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13114     */
13115     /**
13116     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13117     * on any HTTP request
13118     */
13119     /**
13120     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13121     */
13122     /**
13123     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13124     */
13125     multiSort: false,
13126     /**
13127     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13128     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13129     */
13130     remoteSort : false,
13131
13132     /**
13133     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13134      * loaded or when a record is removed. (defaults to false).
13135     */
13136     pruneModifiedRecords : false,
13137
13138     // private
13139     lastOptions : null,
13140
13141     /**
13142      * Add Records to the Store and fires the add event.
13143      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13144      */
13145     add : function(records){
13146         records = [].concat(records);
13147         for(var i = 0, len = records.length; i < len; i++){
13148             records[i].join(this);
13149         }
13150         var index = this.data.length;
13151         this.data.addAll(records);
13152         this.fireEvent("add", this, records, index);
13153     },
13154
13155     /**
13156      * Remove a Record from the Store and fires the remove event.
13157      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13158      */
13159     remove : function(record){
13160         var index = this.data.indexOf(record);
13161         this.data.removeAt(index);
13162  
13163         if(this.pruneModifiedRecords){
13164             this.modified.remove(record);
13165         }
13166         this.fireEvent("remove", this, record, index);
13167     },
13168
13169     /**
13170      * Remove all Records from the Store and fires the clear event.
13171      */
13172     removeAll : function(){
13173         this.data.clear();
13174         if(this.pruneModifiedRecords){
13175             this.modified = [];
13176         }
13177         this.fireEvent("clear", this);
13178     },
13179
13180     /**
13181      * Inserts Records to the Store at the given index and fires the add event.
13182      * @param {Number} index The start index at which to insert the passed Records.
13183      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13184      */
13185     insert : function(index, records){
13186         records = [].concat(records);
13187         for(var i = 0, len = records.length; i < len; i++){
13188             this.data.insert(index, records[i]);
13189             records[i].join(this);
13190         }
13191         this.fireEvent("add", this, records, index);
13192     },
13193
13194     /**
13195      * Get the index within the cache of the passed Record.
13196      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13197      * @return {Number} The index of the passed Record. Returns -1 if not found.
13198      */
13199     indexOf : function(record){
13200         return this.data.indexOf(record);
13201     },
13202
13203     /**
13204      * Get the index within the cache of the Record with the passed id.
13205      * @param {String} id The id of the Record to find.
13206      * @return {Number} The index of the Record. Returns -1 if not found.
13207      */
13208     indexOfId : function(id){
13209         return this.data.indexOfKey(id);
13210     },
13211
13212     /**
13213      * Get the Record with the specified id.
13214      * @param {String} id The id of the Record to find.
13215      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13216      */
13217     getById : function(id){
13218         return this.data.key(id);
13219     },
13220
13221     /**
13222      * Get the Record at the specified index.
13223      * @param {Number} index The index of the Record to find.
13224      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13225      */
13226     getAt : function(index){
13227         return this.data.itemAt(index);
13228     },
13229
13230     /**
13231      * Returns a range of Records between specified indices.
13232      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13233      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13234      * @return {Roo.data.Record[]} An array of Records
13235      */
13236     getRange : function(start, end){
13237         return this.data.getRange(start, end);
13238     },
13239
13240     // private
13241     storeOptions : function(o){
13242         o = Roo.apply({}, o);
13243         delete o.callback;
13244         delete o.scope;
13245         this.lastOptions = o;
13246     },
13247
13248     /**
13249      * Loads the Record cache from the configured Proxy using the configured Reader.
13250      * <p>
13251      * If using remote paging, then the first load call must specify the <em>start</em>
13252      * and <em>limit</em> properties in the options.params property to establish the initial
13253      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13254      * <p>
13255      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13256      * and this call will return before the new data has been loaded. Perform any post-processing
13257      * in a callback function, or in a "load" event handler.</strong>
13258      * <p>
13259      * @param {Object} options An object containing properties which control loading options:<ul>
13260      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13261      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13262      * passed the following arguments:<ul>
13263      * <li>r : Roo.data.Record[]</li>
13264      * <li>options: Options object from the load call</li>
13265      * <li>success: Boolean success indicator</li></ul></li>
13266      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13267      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13268      * </ul>
13269      */
13270     load : function(options){
13271         options = options || {};
13272         if(this.fireEvent("beforeload", this, options) !== false){
13273             this.storeOptions(options);
13274             var p = Roo.apply(options.params || {}, this.baseParams);
13275             // if meta was not loaded from remote source.. try requesting it.
13276             if (!this.reader.metaFromRemote) {
13277                 p._requestMeta = 1;
13278             }
13279             if(this.sortInfo && this.remoteSort){
13280                 var pn = this.paramNames;
13281                 p[pn["sort"]] = this.sortInfo.field;
13282                 p[pn["dir"]] = this.sortInfo.direction;
13283             }
13284             if (this.multiSort) {
13285                 var pn = this.paramNames;
13286                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13287             }
13288             
13289             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13290         }
13291     },
13292
13293     /**
13294      * Reloads the Record cache from the configured Proxy using the configured Reader and
13295      * the options from the last load operation performed.
13296      * @param {Object} options (optional) An object containing properties which may override the options
13297      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13298      * the most recently used options are reused).
13299      */
13300     reload : function(options){
13301         this.load(Roo.applyIf(options||{}, this.lastOptions));
13302     },
13303
13304     // private
13305     // Called as a callback by the Reader during a load operation.
13306     loadRecords : function(o, options, success){
13307         if(!o || success === false){
13308             if(success !== false){
13309                 this.fireEvent("load", this, [], options, o);
13310             }
13311             if(options.callback){
13312                 options.callback.call(options.scope || this, [], options, false);
13313             }
13314             return;
13315         }
13316         // if data returned failure - throw an exception.
13317         if (o.success === false) {
13318             // show a message if no listener is registered.
13319             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13320                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13321             }
13322             // loadmask wil be hooked into this..
13323             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13324             return;
13325         }
13326         var r = o.records, t = o.totalRecords || r.length;
13327         
13328         this.fireEvent("beforeloadadd", this, r, options, o);
13329         
13330         if(!options || options.add !== true){
13331             if(this.pruneModifiedRecords){
13332                 this.modified = [];
13333             }
13334             for(var i = 0, len = r.length; i < len; i++){
13335                 r[i].join(this);
13336             }
13337             if(this.snapshot){
13338                 this.data = this.snapshot;
13339                 delete this.snapshot;
13340             }
13341             this.data.clear();
13342             this.data.addAll(r);
13343             this.totalLength = t;
13344             this.applySort();
13345             this.fireEvent("datachanged", this);
13346         }else{
13347             this.totalLength = Math.max(t, this.data.length+r.length);
13348             this.add(r);
13349         }
13350         
13351         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13352                 
13353             var e = new Roo.data.Record({});
13354
13355             e.set(this.parent.displayField, this.parent.emptyTitle);
13356             e.set(this.parent.valueField, '');
13357
13358             this.insert(0, e);
13359         }
13360             
13361         this.fireEvent("load", this, r, options, o);
13362         if(options.callback){
13363             options.callback.call(options.scope || this, r, options, true);
13364         }
13365     },
13366
13367
13368     /**
13369      * Loads data from a passed data block. A Reader which understands the format of the data
13370      * must have been configured in the constructor.
13371      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13372      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13373      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13374      */
13375     loadData : function(o, append){
13376         var r = this.reader.readRecords(o);
13377         this.loadRecords(r, {add: append}, true);
13378     },
13379     
13380      /**
13381      * using 'cn' the nested child reader read the child array into it's child stores.
13382      * @param {Object} rec The record with a 'children array
13383      */
13384     loadDataFromChildren : function(rec)
13385     {
13386         this.loadData(this.reader.toLoadData(rec));
13387     },
13388     
13389
13390     /**
13391      * Gets the number of cached records.
13392      * <p>
13393      * <em>If using paging, this may not be the total size of the dataset. If the data object
13394      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13395      * the data set size</em>
13396      */
13397     getCount : function(){
13398         return this.data.length || 0;
13399     },
13400
13401     /**
13402      * Gets the total number of records in the dataset as returned by the server.
13403      * <p>
13404      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13405      * the dataset size</em>
13406      */
13407     getTotalCount : function(){
13408         return this.totalLength || 0;
13409     },
13410
13411     /**
13412      * Returns the sort state of the Store as an object with two properties:
13413      * <pre><code>
13414  field {String} The name of the field by which the Records are sorted
13415  direction {String} The sort order, "ASC" or "DESC"
13416      * </code></pre>
13417      */
13418     getSortState : function(){
13419         return this.sortInfo;
13420     },
13421
13422     // private
13423     applySort : function(){
13424         if(this.sortInfo && !this.remoteSort){
13425             var s = this.sortInfo, f = s.field;
13426             var st = this.fields.get(f).sortType;
13427             var fn = function(r1, r2){
13428                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13429                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13430             };
13431             this.data.sort(s.direction, fn);
13432             if(this.snapshot && this.snapshot != this.data){
13433                 this.snapshot.sort(s.direction, fn);
13434             }
13435         }
13436     },
13437
13438     /**
13439      * Sets the default sort column and order to be used by the next load operation.
13440      * @param {String} fieldName The name of the field to sort by.
13441      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13442      */
13443     setDefaultSort : function(field, dir){
13444         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13445     },
13446
13447     /**
13448      * Sort the Records.
13449      * If remote sorting is used, the sort is performed on the server, and the cache is
13450      * reloaded. If local sorting is used, the cache is sorted internally.
13451      * @param {String} fieldName The name of the field to sort by.
13452      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13453      */
13454     sort : function(fieldName, dir){
13455         var f = this.fields.get(fieldName);
13456         if(!dir){
13457             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13458             
13459             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13460                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13461             }else{
13462                 dir = f.sortDir;
13463             }
13464         }
13465         this.sortToggle[f.name] = dir;
13466         this.sortInfo = {field: f.name, direction: dir};
13467         if(!this.remoteSort){
13468             this.applySort();
13469             this.fireEvent("datachanged", this);
13470         }else{
13471             this.load(this.lastOptions);
13472         }
13473     },
13474
13475     /**
13476      * Calls the specified function for each of the Records in the cache.
13477      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13478      * Returning <em>false</em> aborts and exits the iteration.
13479      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13480      */
13481     each : function(fn, scope){
13482         this.data.each(fn, scope);
13483     },
13484
13485     /**
13486      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13487      * (e.g., during paging).
13488      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13489      */
13490     getModifiedRecords : function(){
13491         return this.modified;
13492     },
13493
13494     // private
13495     createFilterFn : function(property, value, anyMatch){
13496         if(!value.exec){ // not a regex
13497             value = String(value);
13498             if(value.length == 0){
13499                 return false;
13500             }
13501             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13502         }
13503         return function(r){
13504             return value.test(r.data[property]);
13505         };
13506     },
13507
13508     /**
13509      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13510      * @param {String} property A field on your records
13511      * @param {Number} start The record index to start at (defaults to 0)
13512      * @param {Number} end The last record index to include (defaults to length - 1)
13513      * @return {Number} The sum
13514      */
13515     sum : function(property, start, end){
13516         var rs = this.data.items, v = 0;
13517         start = start || 0;
13518         end = (end || end === 0) ? end : rs.length-1;
13519
13520         for(var i = start; i <= end; i++){
13521             v += (rs[i].data[property] || 0);
13522         }
13523         return v;
13524     },
13525
13526     /**
13527      * Filter the records by a specified property.
13528      * @param {String} field A field on your records
13529      * @param {String/RegExp} value Either a string that the field
13530      * should start with or a RegExp to test against the field
13531      * @param {Boolean} anyMatch True to match any part not just the beginning
13532      */
13533     filter : function(property, value, anyMatch){
13534         var fn = this.createFilterFn(property, value, anyMatch);
13535         return fn ? this.filterBy(fn) : this.clearFilter();
13536     },
13537
13538     /**
13539      * Filter by a function. The specified function will be called with each
13540      * record in this data source. If the function returns true the record is included,
13541      * otherwise it is filtered.
13542      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13543      * @param {Object} scope (optional) The scope of the function (defaults to this)
13544      */
13545     filterBy : function(fn, scope){
13546         this.snapshot = this.snapshot || this.data;
13547         this.data = this.queryBy(fn, scope||this);
13548         this.fireEvent("datachanged", this);
13549     },
13550
13551     /**
13552      * Query the records by a specified property.
13553      * @param {String} field A field on your records
13554      * @param {String/RegExp} value Either a string that the field
13555      * should start with or a RegExp to test against the field
13556      * @param {Boolean} anyMatch True to match any part not just the beginning
13557      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13558      */
13559     query : function(property, value, anyMatch){
13560         var fn = this.createFilterFn(property, value, anyMatch);
13561         return fn ? this.queryBy(fn) : this.data.clone();
13562     },
13563
13564     /**
13565      * Query by a function. The specified function will be called with each
13566      * record in this data source. If the function returns true the record is included
13567      * in the results.
13568      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13569      * @param {Object} scope (optional) The scope of the function (defaults to this)
13570       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13571      **/
13572     queryBy : function(fn, scope){
13573         var data = this.snapshot || this.data;
13574         return data.filterBy(fn, scope||this);
13575     },
13576
13577     /**
13578      * Collects unique values for a particular dataIndex from this store.
13579      * @param {String} dataIndex The property to collect
13580      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13581      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13582      * @return {Array} An array of the unique values
13583      **/
13584     collect : function(dataIndex, allowNull, bypassFilter){
13585         var d = (bypassFilter === true && this.snapshot) ?
13586                 this.snapshot.items : this.data.items;
13587         var v, sv, r = [], l = {};
13588         for(var i = 0, len = d.length; i < len; i++){
13589             v = d[i].data[dataIndex];
13590             sv = String(v);
13591             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13592                 l[sv] = true;
13593                 r[r.length] = v;
13594             }
13595         }
13596         return r;
13597     },
13598
13599     /**
13600      * Revert to a view of the Record cache with no filtering applied.
13601      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13602      */
13603     clearFilter : function(suppressEvent){
13604         if(this.snapshot && this.snapshot != this.data){
13605             this.data = this.snapshot;
13606             delete this.snapshot;
13607             if(suppressEvent !== true){
13608                 this.fireEvent("datachanged", this);
13609             }
13610         }
13611     },
13612
13613     // private
13614     afterEdit : function(record){
13615         if(this.modified.indexOf(record) == -1){
13616             this.modified.push(record);
13617         }
13618         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13619     },
13620     
13621     // private
13622     afterReject : function(record){
13623         this.modified.remove(record);
13624         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13625     },
13626
13627     // private
13628     afterCommit : function(record){
13629         this.modified.remove(record);
13630         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13631     },
13632
13633     /**
13634      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13635      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13636      */
13637     commitChanges : function(){
13638         var m = this.modified.slice(0);
13639         this.modified = [];
13640         for(var i = 0, len = m.length; i < len; i++){
13641             m[i].commit();
13642         }
13643     },
13644
13645     /**
13646      * Cancel outstanding changes on all changed records.
13647      */
13648     rejectChanges : function(){
13649         var m = this.modified.slice(0);
13650         this.modified = [];
13651         for(var i = 0, len = m.length; i < len; i++){
13652             m[i].reject();
13653         }
13654     },
13655
13656     onMetaChange : function(meta, rtype, o){
13657         this.recordType = rtype;
13658         this.fields = rtype.prototype.fields;
13659         delete this.snapshot;
13660         this.sortInfo = meta.sortInfo || this.sortInfo;
13661         this.modified = [];
13662         this.fireEvent('metachange', this, this.reader.meta);
13663     },
13664     
13665     moveIndex : function(data, type)
13666     {
13667         var index = this.indexOf(data);
13668         
13669         var newIndex = index + type;
13670         
13671         this.remove(data);
13672         
13673         this.insert(newIndex, data);
13674         
13675     }
13676 });/*
13677  * Based on:
13678  * Ext JS Library 1.1.1
13679  * Copyright(c) 2006-2007, Ext JS, LLC.
13680  *
13681  * Originally Released Under LGPL - original licence link has changed is not relivant.
13682  *
13683  * Fork - LGPL
13684  * <script type="text/javascript">
13685  */
13686
13687 /**
13688  * @class Roo.data.SimpleStore
13689  * @extends Roo.data.Store
13690  * Small helper class to make creating Stores from Array data easier.
13691  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13692  * @cfg {Array} fields An array of field definition objects, or field name strings.
13693  * @cfg {Object} an existing reader (eg. copied from another store)
13694  * @cfg {Array} data The multi-dimensional array of data
13695  * @constructor
13696  * @param {Object} config
13697  */
13698 Roo.data.SimpleStore = function(config)
13699 {
13700     Roo.data.SimpleStore.superclass.constructor.call(this, {
13701         isLocal : true,
13702         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13703                 id: config.id
13704             },
13705             Roo.data.Record.create(config.fields)
13706         ),
13707         proxy : new Roo.data.MemoryProxy(config.data)
13708     });
13709     this.load();
13710 };
13711 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13712  * Based on:
13713  * Ext JS Library 1.1.1
13714  * Copyright(c) 2006-2007, Ext JS, LLC.
13715  *
13716  * Originally Released Under LGPL - original licence link has changed is not relivant.
13717  *
13718  * Fork - LGPL
13719  * <script type="text/javascript">
13720  */
13721
13722 /**
13723 /**
13724  * @extends Roo.data.Store
13725  * @class Roo.data.JsonStore
13726  * Small helper class to make creating Stores for JSON data easier. <br/>
13727 <pre><code>
13728 var store = new Roo.data.JsonStore({
13729     url: 'get-images.php',
13730     root: 'images',
13731     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13732 });
13733 </code></pre>
13734  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13735  * JsonReader and HttpProxy (unless inline data is provided).</b>
13736  * @cfg {Array} fields An array of field definition objects, or field name strings.
13737  * @constructor
13738  * @param {Object} config
13739  */
13740 Roo.data.JsonStore = function(c){
13741     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13742         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13743         reader: new Roo.data.JsonReader(c, c.fields)
13744     }));
13745 };
13746 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13747  * Based on:
13748  * Ext JS Library 1.1.1
13749  * Copyright(c) 2006-2007, Ext JS, LLC.
13750  *
13751  * Originally Released Under LGPL - original licence link has changed is not relivant.
13752  *
13753  * Fork - LGPL
13754  * <script type="text/javascript">
13755  */
13756
13757  
13758 Roo.data.Field = function(config){
13759     if(typeof config == "string"){
13760         config = {name: config};
13761     }
13762     Roo.apply(this, config);
13763     
13764     if(!this.type){
13765         this.type = "auto";
13766     }
13767     
13768     var st = Roo.data.SortTypes;
13769     // named sortTypes are supported, here we look them up
13770     if(typeof this.sortType == "string"){
13771         this.sortType = st[this.sortType];
13772     }
13773     
13774     // set default sortType for strings and dates
13775     if(!this.sortType){
13776         switch(this.type){
13777             case "string":
13778                 this.sortType = st.asUCString;
13779                 break;
13780             case "date":
13781                 this.sortType = st.asDate;
13782                 break;
13783             default:
13784                 this.sortType = st.none;
13785         }
13786     }
13787
13788     // define once
13789     var stripRe = /[\$,%]/g;
13790
13791     // prebuilt conversion function for this field, instead of
13792     // switching every time we're reading a value
13793     if(!this.convert){
13794         var cv, dateFormat = this.dateFormat;
13795         switch(this.type){
13796             case "":
13797             case "auto":
13798             case undefined:
13799                 cv = function(v){ return v; };
13800                 break;
13801             case "string":
13802                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13803                 break;
13804             case "int":
13805                 cv = function(v){
13806                     return v !== undefined && v !== null && v !== '' ?
13807                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13808                     };
13809                 break;
13810             case "float":
13811                 cv = function(v){
13812                     return v !== undefined && v !== null && v !== '' ?
13813                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13814                     };
13815                 break;
13816             case "bool":
13817             case "boolean":
13818                 cv = function(v){ return v === true || v === "true" || v == 1; };
13819                 break;
13820             case "date":
13821                 cv = function(v){
13822                     if(!v){
13823                         return '';
13824                     }
13825                     if(v instanceof Date){
13826                         return v;
13827                     }
13828                     if(dateFormat){
13829                         if(dateFormat == "timestamp"){
13830                             return new Date(v*1000);
13831                         }
13832                         return Date.parseDate(v, dateFormat);
13833                     }
13834                     var parsed = Date.parse(v);
13835                     return parsed ? new Date(parsed) : null;
13836                 };
13837              break;
13838             
13839         }
13840         this.convert = cv;
13841     }
13842 };
13843
13844 Roo.data.Field.prototype = {
13845     dateFormat: null,
13846     defaultValue: "",
13847     mapping: null,
13848     sortType : null,
13849     sortDir : "ASC"
13850 };/*
13851  * Based on:
13852  * Ext JS Library 1.1.1
13853  * Copyright(c) 2006-2007, Ext JS, LLC.
13854  *
13855  * Originally Released Under LGPL - original licence link has changed is not relivant.
13856  *
13857  * Fork - LGPL
13858  * <script type="text/javascript">
13859  */
13860  
13861 // Base class for reading structured data from a data source.  This class is intended to be
13862 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13863
13864 /**
13865  * @class Roo.data.DataReader
13866  * Base class for reading structured data from a data source.  This class is intended to be
13867  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13868  */
13869
13870 Roo.data.DataReader = function(meta, recordType){
13871     
13872     this.meta = meta;
13873     
13874     this.recordType = recordType instanceof Array ? 
13875         Roo.data.Record.create(recordType) : recordType;
13876 };
13877
13878 Roo.data.DataReader.prototype = {
13879     
13880     
13881     readerType : 'Data',
13882      /**
13883      * Create an empty record
13884      * @param {Object} data (optional) - overlay some values
13885      * @return {Roo.data.Record} record created.
13886      */
13887     newRow :  function(d) {
13888         var da =  {};
13889         this.recordType.prototype.fields.each(function(c) {
13890             switch( c.type) {
13891                 case 'int' : da[c.name] = 0; break;
13892                 case 'date' : da[c.name] = new Date(); break;
13893                 case 'float' : da[c.name] = 0.0; break;
13894                 case 'boolean' : da[c.name] = false; break;
13895                 default : da[c.name] = ""; break;
13896             }
13897             
13898         });
13899         return new this.recordType(Roo.apply(da, d));
13900     }
13901     
13902     
13903 };/*
13904  * Based on:
13905  * Ext JS Library 1.1.1
13906  * Copyright(c) 2006-2007, Ext JS, LLC.
13907  *
13908  * Originally Released Under LGPL - original licence link has changed is not relivant.
13909  *
13910  * Fork - LGPL
13911  * <script type="text/javascript">
13912  */
13913
13914 /**
13915  * @class Roo.data.DataProxy
13916  * @extends Roo.data.Observable
13917  * This class is an abstract base class for implementations which provide retrieval of
13918  * unformatted data objects.<br>
13919  * <p>
13920  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13921  * (of the appropriate type which knows how to parse the data object) to provide a block of
13922  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13923  * <p>
13924  * Custom implementations must implement the load method as described in
13925  * {@link Roo.data.HttpProxy#load}.
13926  */
13927 Roo.data.DataProxy = function(){
13928     this.addEvents({
13929         /**
13930          * @event beforeload
13931          * Fires before a network request is made to retrieve a data object.
13932          * @param {Object} This DataProxy object.
13933          * @param {Object} params The params parameter to the load function.
13934          */
13935         beforeload : true,
13936         /**
13937          * @event load
13938          * Fires before the load method's callback is called.
13939          * @param {Object} This DataProxy object.
13940          * @param {Object} o The data object.
13941          * @param {Object} arg The callback argument object passed to the load function.
13942          */
13943         load : true,
13944         /**
13945          * @event loadexception
13946          * Fires if an Exception occurs during data retrieval.
13947          * @param {Object} This DataProxy object.
13948          * @param {Object} o The data object.
13949          * @param {Object} arg The callback argument object passed to the load function.
13950          * @param {Object} e The Exception.
13951          */
13952         loadexception : true
13953     });
13954     Roo.data.DataProxy.superclass.constructor.call(this);
13955 };
13956
13957 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
13958
13959     /**
13960      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
13961      */
13962 /*
13963  * Based on:
13964  * Ext JS Library 1.1.1
13965  * Copyright(c) 2006-2007, Ext JS, LLC.
13966  *
13967  * Originally Released Under LGPL - original licence link has changed is not relivant.
13968  *
13969  * Fork - LGPL
13970  * <script type="text/javascript">
13971  */
13972 /**
13973  * @class Roo.data.MemoryProxy
13974  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
13975  * to the Reader when its load method is called.
13976  * @constructor
13977  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
13978  */
13979 Roo.data.MemoryProxy = function(data){
13980     if (data.data) {
13981         data = data.data;
13982     }
13983     Roo.data.MemoryProxy.superclass.constructor.call(this);
13984     this.data = data;
13985 };
13986
13987 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
13988     
13989     /**
13990      * Load data from the requested source (in this case an in-memory
13991      * data object passed to the constructor), read the data object into
13992      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
13993      * process that block using the passed callback.
13994      * @param {Object} params This parameter is not used by the MemoryProxy class.
13995      * @param {Roo.data.DataReader} reader The Reader object which converts the data
13996      * object into a block of Roo.data.Records.
13997      * @param {Function} callback The function into which to pass the block of Roo.data.records.
13998      * The function must be passed <ul>
13999      * <li>The Record block object</li>
14000      * <li>The "arg" argument from the load function</li>
14001      * <li>A boolean success indicator</li>
14002      * </ul>
14003      * @param {Object} scope The scope in which to call the callback
14004      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14005      */
14006     load : function(params, reader, callback, scope, arg){
14007         params = params || {};
14008         var result;
14009         try {
14010             result = reader.readRecords(params.data ? params.data :this.data);
14011         }catch(e){
14012             this.fireEvent("loadexception", this, arg, null, e);
14013             callback.call(scope, null, arg, false);
14014             return;
14015         }
14016         callback.call(scope, result, arg, true);
14017     },
14018     
14019     // private
14020     update : function(params, records){
14021         
14022     }
14023 });/*
14024  * Based on:
14025  * Ext JS Library 1.1.1
14026  * Copyright(c) 2006-2007, Ext JS, LLC.
14027  *
14028  * Originally Released Under LGPL - original licence link has changed is not relivant.
14029  *
14030  * Fork - LGPL
14031  * <script type="text/javascript">
14032  */
14033 /**
14034  * @class Roo.data.HttpProxy
14035  * @extends Roo.data.DataProxy
14036  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14037  * configured to reference a certain URL.<br><br>
14038  * <p>
14039  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14040  * from which the running page was served.<br><br>
14041  * <p>
14042  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14043  * <p>
14044  * Be aware that to enable the browser to parse an XML document, the server must set
14045  * the Content-Type header in the HTTP response to "text/xml".
14046  * @constructor
14047  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14048  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14049  * will be used to make the request.
14050  */
14051 Roo.data.HttpProxy = function(conn){
14052     Roo.data.HttpProxy.superclass.constructor.call(this);
14053     // is conn a conn config or a real conn?
14054     this.conn = conn;
14055     this.useAjax = !conn || !conn.events;
14056   
14057 };
14058
14059 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14060     // thse are take from connection...
14061     
14062     /**
14063      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14064      */
14065     /**
14066      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14067      * extra parameters to each request made by this object. (defaults to undefined)
14068      */
14069     /**
14070      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14071      *  to each request made by this object. (defaults to undefined)
14072      */
14073     /**
14074      * @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)
14075      */
14076     /**
14077      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14078      */
14079      /**
14080      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14081      * @type Boolean
14082      */
14083   
14084
14085     /**
14086      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14087      * @type Boolean
14088      */
14089     /**
14090      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14091      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14092      * a finer-grained basis than the DataProxy events.
14093      */
14094     getConnection : function(){
14095         return this.useAjax ? Roo.Ajax : this.conn;
14096     },
14097
14098     /**
14099      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14100      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14101      * process that block using the passed callback.
14102      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14103      * for the request to the remote server.
14104      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14105      * object into a block of Roo.data.Records.
14106      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14107      * The function must be passed <ul>
14108      * <li>The Record block object</li>
14109      * <li>The "arg" argument from the load function</li>
14110      * <li>A boolean success indicator</li>
14111      * </ul>
14112      * @param {Object} scope The scope in which to call the callback
14113      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14114      */
14115     load : function(params, reader, callback, scope, arg){
14116         if(this.fireEvent("beforeload", this, params) !== false){
14117             var  o = {
14118                 params : params || {},
14119                 request: {
14120                     callback : callback,
14121                     scope : scope,
14122                     arg : arg
14123                 },
14124                 reader: reader,
14125                 callback : this.loadResponse,
14126                 scope: this
14127             };
14128             if(this.useAjax){
14129                 Roo.applyIf(o, this.conn);
14130                 if(this.activeRequest){
14131                     Roo.Ajax.abort(this.activeRequest);
14132                 }
14133                 this.activeRequest = Roo.Ajax.request(o);
14134             }else{
14135                 this.conn.request(o);
14136             }
14137         }else{
14138             callback.call(scope||this, null, arg, false);
14139         }
14140     },
14141
14142     // private
14143     loadResponse : function(o, success, response){
14144         delete this.activeRequest;
14145         if(!success){
14146             this.fireEvent("loadexception", this, o, response);
14147             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14148             return;
14149         }
14150         var result;
14151         try {
14152             result = o.reader.read(response);
14153         }catch(e){
14154             this.fireEvent("loadexception", this, o, response, e);
14155             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14156             return;
14157         }
14158         
14159         this.fireEvent("load", this, o, o.request.arg);
14160         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14161     },
14162
14163     // private
14164     update : function(dataSet){
14165
14166     },
14167
14168     // private
14169     updateResponse : function(dataSet){
14170
14171     }
14172 });/*
14173  * Based on:
14174  * Ext JS Library 1.1.1
14175  * Copyright(c) 2006-2007, Ext JS, LLC.
14176  *
14177  * Originally Released Under LGPL - original licence link has changed is not relivant.
14178  *
14179  * Fork - LGPL
14180  * <script type="text/javascript">
14181  */
14182
14183 /**
14184  * @class Roo.data.ScriptTagProxy
14185  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14186  * other than the originating domain of the running page.<br><br>
14187  * <p>
14188  * <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
14189  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14190  * <p>
14191  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14192  * source code that is used as the source inside a &lt;script> tag.<br><br>
14193  * <p>
14194  * In order for the browser to process the returned data, the server must wrap the data object
14195  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14196  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14197  * depending on whether the callback name was passed:
14198  * <p>
14199  * <pre><code>
14200 boolean scriptTag = false;
14201 String cb = request.getParameter("callback");
14202 if (cb != null) {
14203     scriptTag = true;
14204     response.setContentType("text/javascript");
14205 } else {
14206     response.setContentType("application/x-json");
14207 }
14208 Writer out = response.getWriter();
14209 if (scriptTag) {
14210     out.write(cb + "(");
14211 }
14212 out.print(dataBlock.toJsonString());
14213 if (scriptTag) {
14214     out.write(");");
14215 }
14216 </pre></code>
14217  *
14218  * @constructor
14219  * @param {Object} config A configuration object.
14220  */
14221 Roo.data.ScriptTagProxy = function(config){
14222     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14223     Roo.apply(this, config);
14224     this.head = document.getElementsByTagName("head")[0];
14225 };
14226
14227 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14228
14229 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14230     /**
14231      * @cfg {String} url The URL from which to request the data object.
14232      */
14233     /**
14234      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14235      */
14236     timeout : 30000,
14237     /**
14238      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14239      * the server the name of the callback function set up by the load call to process the returned data object.
14240      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14241      * javascript output which calls this named function passing the data object as its only parameter.
14242      */
14243     callbackParam : "callback",
14244     /**
14245      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14246      * name to the request.
14247      */
14248     nocache : true,
14249
14250     /**
14251      * Load data from the configured URL, read the data object into
14252      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14253      * process that block using the passed callback.
14254      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14255      * for the request to the remote server.
14256      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14257      * object into a block of Roo.data.Records.
14258      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14259      * The function must be passed <ul>
14260      * <li>The Record block object</li>
14261      * <li>The "arg" argument from the load function</li>
14262      * <li>A boolean success indicator</li>
14263      * </ul>
14264      * @param {Object} scope The scope in which to call the callback
14265      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14266      */
14267     load : function(params, reader, callback, scope, arg){
14268         if(this.fireEvent("beforeload", this, params) !== false){
14269
14270             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14271
14272             var url = this.url;
14273             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14274             if(this.nocache){
14275                 url += "&_dc=" + (new Date().getTime());
14276             }
14277             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14278             var trans = {
14279                 id : transId,
14280                 cb : "stcCallback"+transId,
14281                 scriptId : "stcScript"+transId,
14282                 params : params,
14283                 arg : arg,
14284                 url : url,
14285                 callback : callback,
14286                 scope : scope,
14287                 reader : reader
14288             };
14289             var conn = this;
14290
14291             window[trans.cb] = function(o){
14292                 conn.handleResponse(o, trans);
14293             };
14294
14295             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14296
14297             if(this.autoAbort !== false){
14298                 this.abort();
14299             }
14300
14301             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14302
14303             var script = document.createElement("script");
14304             script.setAttribute("src", url);
14305             script.setAttribute("type", "text/javascript");
14306             script.setAttribute("id", trans.scriptId);
14307             this.head.appendChild(script);
14308
14309             this.trans = trans;
14310         }else{
14311             callback.call(scope||this, null, arg, false);
14312         }
14313     },
14314
14315     // private
14316     isLoading : function(){
14317         return this.trans ? true : false;
14318     },
14319
14320     /**
14321      * Abort the current server request.
14322      */
14323     abort : function(){
14324         if(this.isLoading()){
14325             this.destroyTrans(this.trans);
14326         }
14327     },
14328
14329     // private
14330     destroyTrans : function(trans, isLoaded){
14331         this.head.removeChild(document.getElementById(trans.scriptId));
14332         clearTimeout(trans.timeoutId);
14333         if(isLoaded){
14334             window[trans.cb] = undefined;
14335             try{
14336                 delete window[trans.cb];
14337             }catch(e){}
14338         }else{
14339             // if hasn't been loaded, wait for load to remove it to prevent script error
14340             window[trans.cb] = function(){
14341                 window[trans.cb] = undefined;
14342                 try{
14343                     delete window[trans.cb];
14344                 }catch(e){}
14345             };
14346         }
14347     },
14348
14349     // private
14350     handleResponse : function(o, trans){
14351         this.trans = false;
14352         this.destroyTrans(trans, true);
14353         var result;
14354         try {
14355             result = trans.reader.readRecords(o);
14356         }catch(e){
14357             this.fireEvent("loadexception", this, o, trans.arg, e);
14358             trans.callback.call(trans.scope||window, null, trans.arg, false);
14359             return;
14360         }
14361         this.fireEvent("load", this, o, trans.arg);
14362         trans.callback.call(trans.scope||window, result, trans.arg, true);
14363     },
14364
14365     // private
14366     handleFailure : function(trans){
14367         this.trans = false;
14368         this.destroyTrans(trans, false);
14369         this.fireEvent("loadexception", this, null, trans.arg);
14370         trans.callback.call(trans.scope||window, null, trans.arg, false);
14371     }
14372 });/*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382
14383 /**
14384  * @class Roo.data.JsonReader
14385  * @extends Roo.data.DataReader
14386  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14387  * based on mappings in a provided Roo.data.Record constructor.
14388  * 
14389  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14390  * in the reply previously. 
14391  * 
14392  * <p>
14393  * Example code:
14394  * <pre><code>
14395 var RecordDef = Roo.data.Record.create([
14396     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14397     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14398 ]);
14399 var myReader = new Roo.data.JsonReader({
14400     totalProperty: "results",    // The property which contains the total dataset size (optional)
14401     root: "rows",                // The property which contains an Array of row objects
14402     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14403 }, RecordDef);
14404 </code></pre>
14405  * <p>
14406  * This would consume a JSON file like this:
14407  * <pre><code>
14408 { 'results': 2, 'rows': [
14409     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14410     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14411 }
14412 </code></pre>
14413  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14414  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14415  * paged from the remote server.
14416  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14417  * @cfg {String} root name of the property which contains the Array of row objects.
14418  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14419  * @cfg {Array} fields Array of field definition objects
14420  * @constructor
14421  * Create a new JsonReader
14422  * @param {Object} meta Metadata configuration options
14423  * @param {Object} recordType Either an Array of field definition objects,
14424  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14425  */
14426 Roo.data.JsonReader = function(meta, recordType){
14427     
14428     meta = meta || {};
14429     // set some defaults:
14430     Roo.applyIf(meta, {
14431         totalProperty: 'total',
14432         successProperty : 'success',
14433         root : 'data',
14434         id : 'id'
14435     });
14436     
14437     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14438 };
14439 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14440     
14441     readerType : 'Json',
14442     
14443     /**
14444      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14445      * Used by Store query builder to append _requestMeta to params.
14446      * 
14447      */
14448     metaFromRemote : false,
14449     /**
14450      * This method is only used by a DataProxy which has retrieved data from a remote server.
14451      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14452      * @return {Object} data A data block which is used by an Roo.data.Store object as
14453      * a cache of Roo.data.Records.
14454      */
14455     read : function(response){
14456         var json = response.responseText;
14457        
14458         var o = /* eval:var:o */ eval("("+json+")");
14459         if(!o) {
14460             throw {message: "JsonReader.read: Json object not found"};
14461         }
14462         
14463         if(o.metaData){
14464             
14465             delete this.ef;
14466             this.metaFromRemote = true;
14467             this.meta = o.metaData;
14468             this.recordType = Roo.data.Record.create(o.metaData.fields);
14469             this.onMetaChange(this.meta, this.recordType, o);
14470         }
14471         return this.readRecords(o);
14472     },
14473
14474     // private function a store will implement
14475     onMetaChange : function(meta, recordType, o){
14476
14477     },
14478
14479     /**
14480          * @ignore
14481          */
14482     simpleAccess: function(obj, subsc) {
14483         return obj[subsc];
14484     },
14485
14486         /**
14487          * @ignore
14488          */
14489     getJsonAccessor: function(){
14490         var re = /[\[\.]/;
14491         return function(expr) {
14492             try {
14493                 return(re.test(expr))
14494                     ? new Function("obj", "return obj." + expr)
14495                     : function(obj){
14496                         return obj[expr];
14497                     };
14498             } catch(e){}
14499             return Roo.emptyFn;
14500         };
14501     }(),
14502
14503     /**
14504      * Create a data block containing Roo.data.Records from an XML document.
14505      * @param {Object} o An object which contains an Array of row objects in the property specified
14506      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14507      * which contains the total size of the dataset.
14508      * @return {Object} data A data block which is used by an Roo.data.Store object as
14509      * a cache of Roo.data.Records.
14510      */
14511     readRecords : function(o){
14512         /**
14513          * After any data loads, the raw JSON data is available for further custom processing.
14514          * @type Object
14515          */
14516         this.o = o;
14517         var s = this.meta, Record = this.recordType,
14518             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14519
14520 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14521         if (!this.ef) {
14522             if(s.totalProperty) {
14523                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14524                 }
14525                 if(s.successProperty) {
14526                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14527                 }
14528                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14529                 if (s.id) {
14530                         var g = this.getJsonAccessor(s.id);
14531                         this.getId = function(rec) {
14532                                 var r = g(rec);  
14533                                 return (r === undefined || r === "") ? null : r;
14534                         };
14535                 } else {
14536                         this.getId = function(){return null;};
14537                 }
14538             this.ef = [];
14539             for(var jj = 0; jj < fl; jj++){
14540                 f = fi[jj];
14541                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14542                 this.ef[jj] = this.getJsonAccessor(map);
14543             }
14544         }
14545
14546         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14547         if(s.totalProperty){
14548             var vt = parseInt(this.getTotal(o), 10);
14549             if(!isNaN(vt)){
14550                 totalRecords = vt;
14551             }
14552         }
14553         if(s.successProperty){
14554             var vs = this.getSuccess(o);
14555             if(vs === false || vs === 'false'){
14556                 success = false;
14557             }
14558         }
14559         var records = [];
14560         for(var i = 0; i < c; i++){
14561                 var n = root[i];
14562             var values = {};
14563             var id = this.getId(n);
14564             for(var j = 0; j < fl; j++){
14565                 f = fi[j];
14566             var v = this.ef[j](n);
14567             if (!f.convert) {
14568                 Roo.log('missing convert for ' + f.name);
14569                 Roo.log(f);
14570                 continue;
14571             }
14572             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14573             }
14574             var record = new Record(values, id);
14575             record.json = n;
14576             records[i] = record;
14577         }
14578         return {
14579             raw : o,
14580             success : success,
14581             records : records,
14582             totalRecords : totalRecords
14583         };
14584     },
14585     // used when loading children.. @see loadDataFromChildren
14586     toLoadData: function(rec)
14587     {
14588         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14589         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14590         return { data : data, total : data.length };
14591         
14592     }
14593 });/*
14594  * Based on:
14595  * Ext JS Library 1.1.1
14596  * Copyright(c) 2006-2007, Ext JS, LLC.
14597  *
14598  * Originally Released Under LGPL - original licence link has changed is not relivant.
14599  *
14600  * Fork - LGPL
14601  * <script type="text/javascript">
14602  */
14603
14604 /**
14605  * @class Roo.data.ArrayReader
14606  * @extends Roo.data.DataReader
14607  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14608  * Each element of that Array represents a row of data fields. The
14609  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14610  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14611  * <p>
14612  * Example code:.
14613  * <pre><code>
14614 var RecordDef = Roo.data.Record.create([
14615     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14616     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14617 ]);
14618 var myReader = new Roo.data.ArrayReader({
14619     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14620 }, RecordDef);
14621 </code></pre>
14622  * <p>
14623  * This would consume an Array like this:
14624  * <pre><code>
14625 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14626   </code></pre>
14627  
14628  * @constructor
14629  * Create a new JsonReader
14630  * @param {Object} meta Metadata configuration options.
14631  * @param {Object|Array} recordType Either an Array of field definition objects
14632  * 
14633  * @cfg {Array} fields Array of field definition objects
14634  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14635  * as specified to {@link Roo.data.Record#create},
14636  * or an {@link Roo.data.Record} object
14637  *
14638  * 
14639  * created using {@link Roo.data.Record#create}.
14640  */
14641 Roo.data.ArrayReader = function(meta, recordType)
14642 {    
14643     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14644 };
14645
14646 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14647     
14648       /**
14649      * Create a data block containing Roo.data.Records from an XML document.
14650      * @param {Object} o An Array of row objects which represents the dataset.
14651      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14652      * a cache of Roo.data.Records.
14653      */
14654     readRecords : function(o)
14655     {
14656         var sid = this.meta ? this.meta.id : null;
14657         var recordType = this.recordType, fields = recordType.prototype.fields;
14658         var records = [];
14659         var root = o;
14660         for(var i = 0; i < root.length; i++){
14661                 var n = root[i];
14662             var values = {};
14663             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14664             for(var j = 0, jlen = fields.length; j < jlen; j++){
14665                 var f = fields.items[j];
14666                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14667                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14668                 v = f.convert(v);
14669                 values[f.name] = v;
14670             }
14671             var record = new recordType(values, id);
14672             record.json = n;
14673             records[records.length] = record;
14674         }
14675         return {
14676             records : records,
14677             totalRecords : records.length
14678         };
14679     },
14680     // used when loading children.. @see loadDataFromChildren
14681     toLoadData: function(rec)
14682     {
14683         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14684         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14685         
14686     }
14687     
14688     
14689 });/*
14690  * - LGPL
14691  * * 
14692  */
14693
14694 /**
14695  * @class Roo.bootstrap.ComboBox
14696  * @extends Roo.bootstrap.TriggerField
14697  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14698  * @cfg {Boolean} append (true|false) default false
14699  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14700  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14701  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14702  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14703  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14704  * @cfg {Boolean} animate default true
14705  * @cfg {Boolean} emptyResultText only for touch device
14706  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14707  * @cfg {String} emptyTitle default ''
14708  * @constructor
14709  * Create a new ComboBox.
14710  * @param {Object} config Configuration options
14711  */
14712 Roo.bootstrap.ComboBox = function(config){
14713     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14714     this.addEvents({
14715         /**
14716          * @event expand
14717          * Fires when the dropdown list is expanded
14718         * @param {Roo.bootstrap.ComboBox} combo This combo box
14719         */
14720         'expand' : true,
14721         /**
14722          * @event collapse
14723          * Fires when the dropdown list is collapsed
14724         * @param {Roo.bootstrap.ComboBox} combo This combo box
14725         */
14726         'collapse' : true,
14727         /**
14728          * @event beforeselect
14729          * Fires before a list item is selected. Return false to cancel the selection.
14730         * @param {Roo.bootstrap.ComboBox} combo This combo box
14731         * @param {Roo.data.Record} record The data record returned from the underlying store
14732         * @param {Number} index The index of the selected item in the dropdown list
14733         */
14734         'beforeselect' : true,
14735         /**
14736          * @event select
14737          * Fires when a list item is selected
14738         * @param {Roo.bootstrap.ComboBox} combo This combo box
14739         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14740         * @param {Number} index The index of the selected item in the dropdown list
14741         */
14742         'select' : true,
14743         /**
14744          * @event beforequery
14745          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14746          * The event object passed has these properties:
14747         * @param {Roo.bootstrap.ComboBox} combo This combo box
14748         * @param {String} query The query
14749         * @param {Boolean} forceAll true to force "all" query
14750         * @param {Boolean} cancel true to cancel the query
14751         * @param {Object} e The query event object
14752         */
14753         'beforequery': true,
14754          /**
14755          * @event add
14756          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14757         * @param {Roo.bootstrap.ComboBox} combo This combo box
14758         */
14759         'add' : true,
14760         /**
14761          * @event edit
14762          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14763         * @param {Roo.bootstrap.ComboBox} combo This combo box
14764         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14765         */
14766         'edit' : true,
14767         /**
14768          * @event remove
14769          * Fires when the remove value from the combobox array
14770         * @param {Roo.bootstrap.ComboBox} combo This combo box
14771         */
14772         'remove' : true,
14773         /**
14774          * @event afterremove
14775          * Fires when the remove value from the combobox array
14776         * @param {Roo.bootstrap.ComboBox} combo This combo box
14777         */
14778         'afterremove' : true,
14779         /**
14780          * @event specialfilter
14781          * Fires when specialfilter
14782             * @param {Roo.bootstrap.ComboBox} combo This combo box
14783             */
14784         'specialfilter' : true,
14785         /**
14786          * @event tick
14787          * Fires when tick the element
14788             * @param {Roo.bootstrap.ComboBox} combo This combo box
14789             */
14790         'tick' : true,
14791         /**
14792          * @event touchviewdisplay
14793          * Fires when touch view require special display (default is using displayField)
14794             * @param {Roo.bootstrap.ComboBox} combo This combo box
14795             * @param {Object} cfg set html .
14796             */
14797         'touchviewdisplay' : true
14798         
14799     });
14800     
14801     this.item = [];
14802     this.tickItems = [];
14803     
14804     this.selectedIndex = -1;
14805     if(this.mode == 'local'){
14806         if(config.queryDelay === undefined){
14807             this.queryDelay = 10;
14808         }
14809         if(config.minChars === undefined){
14810             this.minChars = 0;
14811         }
14812     }
14813 };
14814
14815 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14816      
14817     /**
14818      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14819      * rendering into an Roo.Editor, defaults to false)
14820      */
14821     /**
14822      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14823      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14824      */
14825     /**
14826      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14827      */
14828     /**
14829      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14830      * the dropdown list (defaults to undefined, with no header element)
14831      */
14832
14833      /**
14834      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14835      */
14836      
14837      /**
14838      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14839      */
14840     listWidth: undefined,
14841     /**
14842      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14843      * mode = 'remote' or 'text' if mode = 'local')
14844      */
14845     displayField: undefined,
14846     
14847     /**
14848      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14849      * mode = 'remote' or 'value' if mode = 'local'). 
14850      * Note: use of a valueField requires the user make a selection
14851      * in order for a value to be mapped.
14852      */
14853     valueField: undefined,
14854     /**
14855      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14856      */
14857     modalTitle : '',
14858     
14859     /**
14860      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14861      * field's data value (defaults to the underlying DOM element's name)
14862      */
14863     hiddenName: undefined,
14864     /**
14865      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14866      */
14867     listClass: '',
14868     /**
14869      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14870      */
14871     selectedClass: 'active',
14872     
14873     /**
14874      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14875      */
14876     shadow:'sides',
14877     /**
14878      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14879      * anchor positions (defaults to 'tl-bl')
14880      */
14881     listAlign: 'tl-bl?',
14882     /**
14883      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14884      */
14885     maxHeight: 300,
14886     /**
14887      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14888      * query specified by the allQuery config option (defaults to 'query')
14889      */
14890     triggerAction: 'query',
14891     /**
14892      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14893      * (defaults to 4, does not apply if editable = false)
14894      */
14895     minChars : 4,
14896     /**
14897      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14898      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14899      */
14900     typeAhead: false,
14901     /**
14902      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14903      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14904      */
14905     queryDelay: 500,
14906     /**
14907      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14908      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14909      */
14910     pageSize: 0,
14911     /**
14912      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14913      * when editable = true (defaults to false)
14914      */
14915     selectOnFocus:false,
14916     /**
14917      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14918      */
14919     queryParam: 'query',
14920     /**
14921      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14922      * when mode = 'remote' (defaults to 'Loading...')
14923      */
14924     loadingText: 'Loading...',
14925     /**
14926      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14927      */
14928     resizable: false,
14929     /**
14930      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14931      */
14932     handleHeight : 8,
14933     /**
14934      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14935      * traditional select (defaults to true)
14936      */
14937     editable: true,
14938     /**
14939      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14940      */
14941     allQuery: '',
14942     /**
14943      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14944      */
14945     mode: 'remote',
14946     /**
14947      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14948      * listWidth has a higher value)
14949      */
14950     minListWidth : 70,
14951     /**
14952      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
14953      * allow the user to set arbitrary text into the field (defaults to false)
14954      */
14955     forceSelection:false,
14956     /**
14957      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
14958      * if typeAhead = true (defaults to 250)
14959      */
14960     typeAheadDelay : 250,
14961     /**
14962      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
14963      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
14964      */
14965     valueNotFoundText : undefined,
14966     /**
14967      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
14968      */
14969     blockFocus : false,
14970     
14971     /**
14972      * @cfg {Boolean} disableClear Disable showing of clear button.
14973      */
14974     disableClear : false,
14975     /**
14976      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
14977      */
14978     alwaysQuery : false,
14979     
14980     /**
14981      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
14982      */
14983     multiple : false,
14984     
14985     /**
14986      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
14987      */
14988     invalidClass : "has-warning",
14989     
14990     /**
14991      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
14992      */
14993     validClass : "has-success",
14994     
14995     /**
14996      * @cfg {Boolean} specialFilter (true|false) special filter default false
14997      */
14998     specialFilter : false,
14999     
15000     /**
15001      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15002      */
15003     mobileTouchView : true,
15004     
15005     /**
15006      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15007      */
15008     useNativeIOS : false,
15009     
15010     /**
15011      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15012      */
15013     mobile_restrict_height : false,
15014     
15015     ios_options : false,
15016     
15017     //private
15018     addicon : false,
15019     editicon: false,
15020     
15021     page: 0,
15022     hasQuery: false,
15023     append: false,
15024     loadNext: false,
15025     autoFocus : true,
15026     tickable : false,
15027     btnPosition : 'right',
15028     triggerList : true,
15029     showToggleBtn : true,
15030     animate : true,
15031     emptyResultText: 'Empty',
15032     triggerText : 'Select',
15033     emptyTitle : '',
15034     
15035     // element that contains real text value.. (when hidden is used..)
15036     
15037     getAutoCreate : function()
15038     {   
15039         var cfg = false;
15040         //render
15041         /*
15042          * Render classic select for iso
15043          */
15044         
15045         if(Roo.isIOS && this.useNativeIOS){
15046             cfg = this.getAutoCreateNativeIOS();
15047             return cfg;
15048         }
15049         
15050         /*
15051          * Touch Devices
15052          */
15053         
15054         if(Roo.isTouch && this.mobileTouchView){
15055             cfg = this.getAutoCreateTouchView();
15056             return cfg;;
15057         }
15058         
15059         /*
15060          *  Normal ComboBox
15061          */
15062         if(!this.tickable){
15063             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15064             return cfg;
15065         }
15066         
15067         /*
15068          *  ComboBox with tickable selections
15069          */
15070              
15071         var align = this.labelAlign || this.parentLabelAlign();
15072         
15073         cfg = {
15074             cls : 'form-group roo-combobox-tickable' //input-group
15075         };
15076         
15077         var btn_text_select = '';
15078         var btn_text_done = '';
15079         var btn_text_cancel = '';
15080         
15081         if (this.btn_text_show) {
15082             btn_text_select = 'Select';
15083             btn_text_done = 'Done';
15084             btn_text_cancel = 'Cancel'; 
15085         }
15086         
15087         var buttons = {
15088             tag : 'div',
15089             cls : 'tickable-buttons',
15090             cn : [
15091                 {
15092                     tag : 'button',
15093                     type : 'button',
15094                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15095                     //html : this.triggerText
15096                     html: btn_text_select
15097                 },
15098                 {
15099                     tag : 'button',
15100                     type : 'button',
15101                     name : 'ok',
15102                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15103                     //html : 'Done'
15104                     html: btn_text_done
15105                 },
15106                 {
15107                     tag : 'button',
15108                     type : 'button',
15109                     name : 'cancel',
15110                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15111                     //html : 'Cancel'
15112                     html: btn_text_cancel
15113                 }
15114             ]
15115         };
15116         
15117         if(this.editable){
15118             buttons.cn.unshift({
15119                 tag: 'input',
15120                 cls: 'roo-select2-search-field-input'
15121             });
15122         }
15123         
15124         var _this = this;
15125         
15126         Roo.each(buttons.cn, function(c){
15127             if (_this.size) {
15128                 c.cls += ' btn-' + _this.size;
15129             }
15130
15131             if (_this.disabled) {
15132                 c.disabled = true;
15133             }
15134         });
15135         
15136         var box = {
15137             tag: 'div',
15138             style : 'display: contents',
15139             cn: [
15140                 {
15141                     tag: 'input',
15142                     type : 'hidden',
15143                     cls: 'form-hidden-field'
15144                 },
15145                 {
15146                     tag: 'ul',
15147                     cls: 'roo-select2-choices',
15148                     cn:[
15149                         {
15150                             tag: 'li',
15151                             cls: 'roo-select2-search-field',
15152                             cn: [
15153                                 buttons
15154                             ]
15155                         }
15156                     ]
15157                 }
15158             ]
15159         };
15160         
15161         var combobox = {
15162             cls: 'roo-select2-container input-group roo-select2-container-multi',
15163             cn: [
15164                 
15165                 box
15166 //                {
15167 //                    tag: 'ul',
15168 //                    cls: 'typeahead typeahead-long dropdown-menu',
15169 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15170 //                }
15171             ]
15172         };
15173         
15174         if(this.hasFeedback && !this.allowBlank){
15175             
15176             var feedback = {
15177                 tag: 'span',
15178                 cls: 'glyphicon form-control-feedback'
15179             };
15180
15181             combobox.cn.push(feedback);
15182         }
15183         
15184         
15185         
15186         var indicator = {
15187             tag : 'i',
15188             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15189             tooltip : 'This field is required'
15190         };
15191         if (Roo.bootstrap.version == 4) {
15192             indicator = {
15193                 tag : 'i',
15194                 style : 'display:none'
15195             };
15196         }
15197         if (align ==='left' && this.fieldLabel.length) {
15198             
15199             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15200             
15201             cfg.cn = [
15202                 indicator,
15203                 {
15204                     tag: 'label',
15205                     'for' :  id,
15206                     cls : 'control-label col-form-label',
15207                     html : this.fieldLabel
15208
15209                 },
15210                 {
15211                     cls : "", 
15212                     cn: [
15213                         combobox
15214                     ]
15215                 }
15216
15217             ];
15218             
15219             var labelCfg = cfg.cn[1];
15220             var contentCfg = cfg.cn[2];
15221             
15222
15223             if(this.indicatorpos == 'right'){
15224                 
15225                 cfg.cn = [
15226                     {
15227                         tag: 'label',
15228                         'for' :  id,
15229                         cls : 'control-label col-form-label',
15230                         cn : [
15231                             {
15232                                 tag : 'span',
15233                                 html : this.fieldLabel
15234                             },
15235                             indicator
15236                         ]
15237                     },
15238                     {
15239                         cls : "",
15240                         cn: [
15241                             combobox
15242                         ]
15243                     }
15244
15245                 ];
15246                 
15247                 
15248                 
15249                 labelCfg = cfg.cn[0];
15250                 contentCfg = cfg.cn[1];
15251             
15252             }
15253             
15254             if(this.labelWidth > 12){
15255                 labelCfg.style = "width: " + this.labelWidth + 'px';
15256             }
15257             
15258             if(this.labelWidth < 13 && this.labelmd == 0){
15259                 this.labelmd = this.labelWidth;
15260             }
15261             
15262             if(this.labellg > 0){
15263                 labelCfg.cls += ' col-lg-' + this.labellg;
15264                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15265             }
15266             
15267             if(this.labelmd > 0){
15268                 labelCfg.cls += ' col-md-' + this.labelmd;
15269                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15270             }
15271             
15272             if(this.labelsm > 0){
15273                 labelCfg.cls += ' col-sm-' + this.labelsm;
15274                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15275             }
15276             
15277             if(this.labelxs > 0){
15278                 labelCfg.cls += ' col-xs-' + this.labelxs;
15279                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15280             }
15281                 
15282                 
15283         } else if ( this.fieldLabel.length) {
15284 //                Roo.log(" label");
15285                  cfg.cn = [
15286                    indicator,
15287                     {
15288                         tag: 'label',
15289                         //cls : 'input-group-addon',
15290                         html : this.fieldLabel
15291                     },
15292                     combobox
15293                 ];
15294                 
15295                 if(this.indicatorpos == 'right'){
15296                     cfg.cn = [
15297                         {
15298                             tag: 'label',
15299                             //cls : 'input-group-addon',
15300                             html : this.fieldLabel
15301                         },
15302                         indicator,
15303                         combobox
15304                     ];
15305                     
15306                 }
15307
15308         } else {
15309             
15310 //                Roo.log(" no label && no align");
15311                 cfg = combobox
15312                      
15313                 
15314         }
15315          
15316         var settings=this;
15317         ['xs','sm','md','lg'].map(function(size){
15318             if (settings[size]) {
15319                 cfg.cls += ' col-' + size + '-' + settings[size];
15320             }
15321         });
15322         
15323         return cfg;
15324         
15325     },
15326     
15327     _initEventsCalled : false,
15328     
15329     // private
15330     initEvents: function()
15331     {   
15332         if (this._initEventsCalled) { // as we call render... prevent looping...
15333             return;
15334         }
15335         this._initEventsCalled = true;
15336         
15337         if (!this.store) {
15338             throw "can not find store for combo";
15339         }
15340         
15341         this.indicator = this.indicatorEl();
15342         
15343         this.store = Roo.factory(this.store, Roo.data);
15344         this.store.parent = this;
15345         
15346         // if we are building from html. then this element is so complex, that we can not really
15347         // use the rendered HTML.
15348         // so we have to trash and replace the previous code.
15349         if (Roo.XComponent.build_from_html) {
15350             // remove this element....
15351             var e = this.el.dom, k=0;
15352             while (e ) { e = e.previousSibling;  ++k;}
15353
15354             this.el.remove();
15355             
15356             this.el=false;
15357             this.rendered = false;
15358             
15359             this.render(this.parent().getChildContainer(true), k);
15360         }
15361         
15362         if(Roo.isIOS && this.useNativeIOS){
15363             this.initIOSView();
15364             return;
15365         }
15366         
15367         /*
15368          * Touch Devices
15369          */
15370         
15371         if(Roo.isTouch && this.mobileTouchView){
15372             this.initTouchView();
15373             return;
15374         }
15375         
15376         if(this.tickable){
15377             this.initTickableEvents();
15378             return;
15379         }
15380         
15381         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15382         
15383         if(this.hiddenName){
15384             
15385             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15386             
15387             this.hiddenField.dom.value =
15388                 this.hiddenValue !== undefined ? this.hiddenValue :
15389                 this.value !== undefined ? this.value : '';
15390
15391             // prevent input submission
15392             this.el.dom.removeAttribute('name');
15393             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15394              
15395              
15396         }
15397         //if(Roo.isGecko){
15398         //    this.el.dom.setAttribute('autocomplete', 'off');
15399         //}
15400         
15401         var cls = 'x-combo-list';
15402         
15403         //this.list = new Roo.Layer({
15404         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15405         //});
15406         
15407         var _this = this;
15408         
15409         (function(){
15410             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15411             _this.list.setWidth(lw);
15412         }).defer(100);
15413         
15414         this.list.on('mouseover', this.onViewOver, this);
15415         this.list.on('mousemove', this.onViewMove, this);
15416         this.list.on('scroll', this.onViewScroll, this);
15417         
15418         /*
15419         this.list.swallowEvent('mousewheel');
15420         this.assetHeight = 0;
15421
15422         if(this.title){
15423             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15424             this.assetHeight += this.header.getHeight();
15425         }
15426
15427         this.innerList = this.list.createChild({cls:cls+'-inner'});
15428         this.innerList.on('mouseover', this.onViewOver, this);
15429         this.innerList.on('mousemove', this.onViewMove, this);
15430         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15431         
15432         if(this.allowBlank && !this.pageSize && !this.disableClear){
15433             this.footer = this.list.createChild({cls:cls+'-ft'});
15434             this.pageTb = new Roo.Toolbar(this.footer);
15435            
15436         }
15437         if(this.pageSize){
15438             this.footer = this.list.createChild({cls:cls+'-ft'});
15439             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15440                     {pageSize: this.pageSize});
15441             
15442         }
15443         
15444         if (this.pageTb && this.allowBlank && !this.disableClear) {
15445             var _this = this;
15446             this.pageTb.add(new Roo.Toolbar.Fill(), {
15447                 cls: 'x-btn-icon x-btn-clear',
15448                 text: '&#160;',
15449                 handler: function()
15450                 {
15451                     _this.collapse();
15452                     _this.clearValue();
15453                     _this.onSelect(false, -1);
15454                 }
15455             });
15456         }
15457         if (this.footer) {
15458             this.assetHeight += this.footer.getHeight();
15459         }
15460         */
15461             
15462         if(!this.tpl){
15463             this.tpl = Roo.bootstrap.version == 4 ?
15464                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15465                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15466         }
15467
15468         this.view = new Roo.View(this.list, this.tpl, {
15469             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15470         });
15471         //this.view.wrapEl.setDisplayed(false);
15472         this.view.on('click', this.onViewClick, this);
15473         
15474         
15475         this.store.on('beforeload', this.onBeforeLoad, this);
15476         this.store.on('load', this.onLoad, this);
15477         this.store.on('loadexception', this.onLoadException, this);
15478         /*
15479         if(this.resizable){
15480             this.resizer = new Roo.Resizable(this.list,  {
15481                pinned:true, handles:'se'
15482             });
15483             this.resizer.on('resize', function(r, w, h){
15484                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15485                 this.listWidth = w;
15486                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15487                 this.restrictHeight();
15488             }, this);
15489             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15490         }
15491         */
15492         if(!this.editable){
15493             this.editable = true;
15494             this.setEditable(false);
15495         }
15496         
15497         /*
15498         
15499         if (typeof(this.events.add.listeners) != 'undefined') {
15500             
15501             this.addicon = this.wrap.createChild(
15502                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15503        
15504             this.addicon.on('click', function(e) {
15505                 this.fireEvent('add', this);
15506             }, this);
15507         }
15508         if (typeof(this.events.edit.listeners) != 'undefined') {
15509             
15510             this.editicon = this.wrap.createChild(
15511                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15512             if (this.addicon) {
15513                 this.editicon.setStyle('margin-left', '40px');
15514             }
15515             this.editicon.on('click', function(e) {
15516                 
15517                 // we fire even  if inothing is selected..
15518                 this.fireEvent('edit', this, this.lastData );
15519                 
15520             }, this);
15521         }
15522         */
15523         
15524         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15525             "up" : function(e){
15526                 this.inKeyMode = true;
15527                 this.selectPrev();
15528             },
15529
15530             "down" : function(e){
15531                 if(!this.isExpanded()){
15532                     this.onTriggerClick();
15533                 }else{
15534                     this.inKeyMode = true;
15535                     this.selectNext();
15536                 }
15537             },
15538
15539             "enter" : function(e){
15540 //                this.onViewClick();
15541                 //return true;
15542                 this.collapse();
15543                 
15544                 if(this.fireEvent("specialkey", this, e)){
15545                     this.onViewClick(false);
15546                 }
15547                 
15548                 return true;
15549             },
15550
15551             "esc" : function(e){
15552                 this.collapse();
15553             },
15554
15555             "tab" : function(e){
15556                 this.collapse();
15557                 
15558                 if(this.fireEvent("specialkey", this, e)){
15559                     this.onViewClick(false);
15560                 }
15561                 
15562                 return true;
15563             },
15564
15565             scope : this,
15566
15567             doRelay : function(foo, bar, hname){
15568                 if(hname == 'down' || this.scope.isExpanded()){
15569                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15570                 }
15571                 return true;
15572             },
15573
15574             forceKeyDown: true
15575         });
15576         
15577         
15578         this.queryDelay = Math.max(this.queryDelay || 10,
15579                 this.mode == 'local' ? 10 : 250);
15580         
15581         
15582         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15583         
15584         if(this.typeAhead){
15585             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15586         }
15587         if(this.editable !== false){
15588             this.inputEl().on("keyup", this.onKeyUp, this);
15589         }
15590         if(this.forceSelection){
15591             this.inputEl().on('blur', this.doForce, this);
15592         }
15593         
15594         if(this.multiple){
15595             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15596             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15597         }
15598     },
15599     
15600     initTickableEvents: function()
15601     {   
15602         this.createList();
15603         
15604         if(this.hiddenName){
15605             
15606             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15607             
15608             this.hiddenField.dom.value =
15609                 this.hiddenValue !== undefined ? this.hiddenValue :
15610                 this.value !== undefined ? this.value : '';
15611
15612             // prevent input submission
15613             this.el.dom.removeAttribute('name');
15614             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15615              
15616              
15617         }
15618         
15619 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15620         
15621         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15622         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15623         if(this.triggerList){
15624             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15625         }
15626          
15627         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15628         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15629         
15630         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15631         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15632         
15633         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15634         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15635         
15636         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15637         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15638         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15639         
15640         this.okBtn.hide();
15641         this.cancelBtn.hide();
15642         
15643         var _this = this;
15644         
15645         (function(){
15646             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15647             _this.list.setWidth(lw);
15648         }).defer(100);
15649         
15650         this.list.on('mouseover', this.onViewOver, this);
15651         this.list.on('mousemove', this.onViewMove, this);
15652         
15653         this.list.on('scroll', this.onViewScroll, this);
15654         
15655         if(!this.tpl){
15656             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15657                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15658         }
15659
15660         this.view = new Roo.View(this.list, this.tpl, {
15661             singleSelect:true,
15662             tickable:true,
15663             parent:this,
15664             store: this.store,
15665             selectedClass: this.selectedClass
15666         });
15667         
15668         //this.view.wrapEl.setDisplayed(false);
15669         this.view.on('click', this.onViewClick, this);
15670         
15671         
15672         
15673         this.store.on('beforeload', this.onBeforeLoad, this);
15674         this.store.on('load', this.onLoad, this);
15675         this.store.on('loadexception', this.onLoadException, this);
15676         
15677         if(this.editable){
15678             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15679                 "up" : function(e){
15680                     this.inKeyMode = true;
15681                     this.selectPrev();
15682                 },
15683
15684                 "down" : function(e){
15685                     this.inKeyMode = true;
15686                     this.selectNext();
15687                 },
15688
15689                 "enter" : function(e){
15690                     if(this.fireEvent("specialkey", this, e)){
15691                         this.onViewClick(false);
15692                     }
15693                     
15694                     return true;
15695                 },
15696
15697                 "esc" : function(e){
15698                     this.onTickableFooterButtonClick(e, false, false);
15699                 },
15700
15701                 "tab" : function(e){
15702                     this.fireEvent("specialkey", this, e);
15703                     
15704                     this.onTickableFooterButtonClick(e, false, false);
15705                     
15706                     return true;
15707                 },
15708
15709                 scope : this,
15710
15711                 doRelay : function(e, fn, key){
15712                     if(this.scope.isExpanded()){
15713                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15714                     }
15715                     return true;
15716                 },
15717
15718                 forceKeyDown: true
15719             });
15720         }
15721         
15722         this.queryDelay = Math.max(this.queryDelay || 10,
15723                 this.mode == 'local' ? 10 : 250);
15724         
15725         
15726         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15727         
15728         if(this.typeAhead){
15729             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15730         }
15731         
15732         if(this.editable !== false){
15733             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15734         }
15735         
15736         this.indicator = this.indicatorEl();
15737         
15738         if(this.indicator){
15739             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15740             this.indicator.hide();
15741         }
15742         
15743     },
15744
15745     onDestroy : function(){
15746         if(this.view){
15747             this.view.setStore(null);
15748             this.view.el.removeAllListeners();
15749             this.view.el.remove();
15750             this.view.purgeListeners();
15751         }
15752         if(this.list){
15753             this.list.dom.innerHTML  = '';
15754         }
15755         
15756         if(this.store){
15757             this.store.un('beforeload', this.onBeforeLoad, this);
15758             this.store.un('load', this.onLoad, this);
15759             this.store.un('loadexception', this.onLoadException, this);
15760         }
15761         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15762     },
15763
15764     // private
15765     fireKey : function(e){
15766         if(e.isNavKeyPress() && !this.list.isVisible()){
15767             this.fireEvent("specialkey", this, e);
15768         }
15769     },
15770
15771     // private
15772     onResize: function(w, h){
15773 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15774 //        
15775 //        if(typeof w != 'number'){
15776 //            // we do not handle it!?!?
15777 //            return;
15778 //        }
15779 //        var tw = this.trigger.getWidth();
15780 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15781 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15782 //        var x = w - tw;
15783 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15784 //            
15785 //        //this.trigger.setStyle('left', x+'px');
15786 //        
15787 //        if(this.list && this.listWidth === undefined){
15788 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15789 //            this.list.setWidth(lw);
15790 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15791 //        }
15792         
15793     
15794         
15795     },
15796
15797     /**
15798      * Allow or prevent the user from directly editing the field text.  If false is passed,
15799      * the user will only be able to select from the items defined in the dropdown list.  This method
15800      * is the runtime equivalent of setting the 'editable' config option at config time.
15801      * @param {Boolean} value True to allow the user to directly edit the field text
15802      */
15803     setEditable : function(value){
15804         if(value == this.editable){
15805             return;
15806         }
15807         this.editable = value;
15808         if(!value){
15809             this.inputEl().dom.setAttribute('readOnly', true);
15810             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15811             this.inputEl().addClass('x-combo-noedit');
15812         }else{
15813             this.inputEl().dom.setAttribute('readOnly', false);
15814             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15815             this.inputEl().removeClass('x-combo-noedit');
15816         }
15817     },
15818
15819     // private
15820     
15821     onBeforeLoad : function(combo,opts){
15822         if(!this.hasFocus){
15823             return;
15824         }
15825          if (!opts.add) {
15826             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15827          }
15828         this.restrictHeight();
15829         this.selectedIndex = -1;
15830     },
15831
15832     // private
15833     onLoad : function(){
15834         
15835         this.hasQuery = false;
15836         
15837         if(!this.hasFocus){
15838             return;
15839         }
15840         
15841         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15842             this.loading.hide();
15843         }
15844         
15845         if(this.store.getCount() > 0){
15846             
15847             this.expand();
15848             this.restrictHeight();
15849             if(this.lastQuery == this.allQuery){
15850                 if(this.editable && !this.tickable){
15851                     this.inputEl().dom.select();
15852                 }
15853                 
15854                 if(
15855                     !this.selectByValue(this.value, true) &&
15856                     this.autoFocus && 
15857                     (
15858                         !this.store.lastOptions ||
15859                         typeof(this.store.lastOptions.add) == 'undefined' || 
15860                         this.store.lastOptions.add != true
15861                     )
15862                 ){
15863                     this.select(0, true);
15864                 }
15865             }else{
15866                 if(this.autoFocus){
15867                     this.selectNext();
15868                 }
15869                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15870                     this.taTask.delay(this.typeAheadDelay);
15871                 }
15872             }
15873         }else{
15874             this.onEmptyResults();
15875         }
15876         
15877         //this.el.focus();
15878     },
15879     // private
15880     onLoadException : function()
15881     {
15882         this.hasQuery = false;
15883         
15884         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15885             this.loading.hide();
15886         }
15887         
15888         if(this.tickable && this.editable){
15889             return;
15890         }
15891         
15892         this.collapse();
15893         // only causes errors at present
15894         //Roo.log(this.store.reader.jsonData);
15895         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15896             // fixme
15897             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15898         //}
15899         
15900         
15901     },
15902     // private
15903     onTypeAhead : function(){
15904         if(this.store.getCount() > 0){
15905             var r = this.store.getAt(0);
15906             var newValue = r.data[this.displayField];
15907             var len = newValue.length;
15908             var selStart = this.getRawValue().length;
15909             
15910             if(selStart != len){
15911                 this.setRawValue(newValue);
15912                 this.selectText(selStart, newValue.length);
15913             }
15914         }
15915     },
15916
15917     // private
15918     onSelect : function(record, index){
15919         
15920         if(this.fireEvent('beforeselect', this, record, index) !== false){
15921         
15922             this.setFromData(index > -1 ? record.data : false);
15923             
15924             this.collapse();
15925             this.fireEvent('select', this, record, index);
15926         }
15927     },
15928
15929     /**
15930      * Returns the currently selected field value or empty string if no value is set.
15931      * @return {String} value The selected value
15932      */
15933     getValue : function()
15934     {
15935         if(Roo.isIOS && this.useNativeIOS){
15936             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15937         }
15938         
15939         if(this.multiple){
15940             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15941         }
15942         
15943         if(this.valueField){
15944             return typeof this.value != 'undefined' ? this.value : '';
15945         }else{
15946             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
15947         }
15948     },
15949     
15950     getRawValue : function()
15951     {
15952         if(Roo.isIOS && this.useNativeIOS){
15953             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
15954         }
15955         
15956         var v = this.inputEl().getValue();
15957         
15958         return v;
15959     },
15960
15961     /**
15962      * Clears any text/value currently set in the field
15963      */
15964     clearValue : function(){
15965         
15966         if(this.hiddenField){
15967             this.hiddenField.dom.value = '';
15968         }
15969         this.value = '';
15970         this.setRawValue('');
15971         this.lastSelectionText = '';
15972         this.lastData = false;
15973         
15974         var close = this.closeTriggerEl();
15975         
15976         if(close){
15977             close.hide();
15978         }
15979         
15980         this.validate();
15981         
15982     },
15983
15984     /**
15985      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
15986      * will be displayed in the field.  If the value does not match the data value of an existing item,
15987      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
15988      * Otherwise the field will be blank (although the value will still be set).
15989      * @param {String} value The value to match
15990      */
15991     setValue : function(v)
15992     {
15993         if(Roo.isIOS && this.useNativeIOS){
15994             this.setIOSValue(v);
15995             return;
15996         }
15997         
15998         if(this.multiple){
15999             this.syncValue();
16000             return;
16001         }
16002         
16003         var text = v;
16004         if(this.valueField){
16005             var r = this.findRecord(this.valueField, v);
16006             if(r){
16007                 text = r.data[this.displayField];
16008             }else if(this.valueNotFoundText !== undefined){
16009                 text = this.valueNotFoundText;
16010             }
16011         }
16012         this.lastSelectionText = text;
16013         if(this.hiddenField){
16014             this.hiddenField.dom.value = v;
16015         }
16016         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16017         this.value = v;
16018         
16019         var close = this.closeTriggerEl();
16020         
16021         if(close){
16022             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16023         }
16024         
16025         this.validate();
16026     },
16027     /**
16028      * @property {Object} the last set data for the element
16029      */
16030     
16031     lastData : false,
16032     /**
16033      * Sets the value of the field based on a object which is related to the record format for the store.
16034      * @param {Object} value the value to set as. or false on reset?
16035      */
16036     setFromData : function(o){
16037         
16038         if(this.multiple){
16039             this.addItem(o);
16040             return;
16041         }
16042             
16043         var dv = ''; // display value
16044         var vv = ''; // value value..
16045         this.lastData = o;
16046         if (this.displayField) {
16047             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16048         } else {
16049             // this is an error condition!!!
16050             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16051         }
16052         
16053         if(this.valueField){
16054             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16055         }
16056         
16057         var close = this.closeTriggerEl();
16058         
16059         if(close){
16060             if(dv.length || vv * 1 > 0){
16061                 close.show() ;
16062                 this.blockFocus=true;
16063             } else {
16064                 close.hide();
16065             }             
16066         }
16067         
16068         if(this.hiddenField){
16069             this.hiddenField.dom.value = vv;
16070             
16071             this.lastSelectionText = dv;
16072             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16073             this.value = vv;
16074             return;
16075         }
16076         // no hidden field.. - we store the value in 'value', but still display
16077         // display field!!!!
16078         this.lastSelectionText = dv;
16079         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16080         this.value = vv;
16081         
16082         
16083         
16084     },
16085     // private
16086     reset : function(){
16087         // overridden so that last data is reset..
16088         
16089         if(this.multiple){
16090             this.clearItem();
16091             return;
16092         }
16093         
16094         this.setValue(this.originalValue);
16095         //this.clearInvalid();
16096         this.lastData = false;
16097         if (this.view) {
16098             this.view.clearSelections();
16099         }
16100         
16101         this.validate();
16102     },
16103     // private
16104     findRecord : function(prop, value){
16105         var record;
16106         if(this.store.getCount() > 0){
16107             this.store.each(function(r){
16108                 if(r.data[prop] == value){
16109                     record = r;
16110                     return false;
16111                 }
16112                 return true;
16113             });
16114         }
16115         return record;
16116     },
16117     
16118     getName: function()
16119     {
16120         // returns hidden if it's set..
16121         if (!this.rendered) {return ''};
16122         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16123         
16124     },
16125     // private
16126     onViewMove : function(e, t){
16127         this.inKeyMode = false;
16128     },
16129
16130     // private
16131     onViewOver : function(e, t){
16132         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16133             return;
16134         }
16135         var item = this.view.findItemFromChild(t);
16136         
16137         if(item){
16138             var index = this.view.indexOf(item);
16139             this.select(index, false);
16140         }
16141     },
16142
16143     // private
16144     onViewClick : function(view, doFocus, el, e)
16145     {
16146         var index = this.view.getSelectedIndexes()[0];
16147         
16148         var r = this.store.getAt(index);
16149         
16150         if(this.tickable){
16151             
16152             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16153                 return;
16154             }
16155             
16156             var rm = false;
16157             var _this = this;
16158             
16159             Roo.each(this.tickItems, function(v,k){
16160                 
16161                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16162                     Roo.log(v);
16163                     _this.tickItems.splice(k, 1);
16164                     
16165                     if(typeof(e) == 'undefined' && view == false){
16166                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16167                     }
16168                     
16169                     rm = true;
16170                     return;
16171                 }
16172             });
16173             
16174             if(rm){
16175                 return;
16176             }
16177             
16178             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16179                 this.tickItems.push(r.data);
16180             }
16181             
16182             if(typeof(e) == 'undefined' && view == false){
16183                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16184             }
16185                     
16186             return;
16187         }
16188         
16189         if(r){
16190             this.onSelect(r, index);
16191         }
16192         if(doFocus !== false && !this.blockFocus){
16193             this.inputEl().focus();
16194         }
16195     },
16196
16197     // private
16198     restrictHeight : function(){
16199         //this.innerList.dom.style.height = '';
16200         //var inner = this.innerList.dom;
16201         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16202         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16203         //this.list.beginUpdate();
16204         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16205         this.list.alignTo(this.inputEl(), this.listAlign);
16206         this.list.alignTo(this.inputEl(), this.listAlign);
16207         //this.list.endUpdate();
16208     },
16209
16210     // private
16211     onEmptyResults : function(){
16212         
16213         if(this.tickable && this.editable){
16214             this.hasFocus = false;
16215             this.restrictHeight();
16216             return;
16217         }
16218         
16219         this.collapse();
16220     },
16221
16222     /**
16223      * Returns true if the dropdown list is expanded, else false.
16224      */
16225     isExpanded : function(){
16226         return this.list.isVisible();
16227     },
16228
16229     /**
16230      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16231      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16232      * @param {String} value The data value of the item to select
16233      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16234      * selected item if it is not currently in view (defaults to true)
16235      * @return {Boolean} True if the value matched an item in the list, else false
16236      */
16237     selectByValue : function(v, scrollIntoView){
16238         if(v !== undefined && v !== null){
16239             var r = this.findRecord(this.valueField || this.displayField, v);
16240             if(r){
16241                 this.select(this.store.indexOf(r), scrollIntoView);
16242                 return true;
16243             }
16244         }
16245         return false;
16246     },
16247
16248     /**
16249      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16250      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16251      * @param {Number} index The zero-based index of the list item to select
16252      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16253      * selected item if it is not currently in view (defaults to true)
16254      */
16255     select : function(index, scrollIntoView){
16256         this.selectedIndex = index;
16257         this.view.select(index);
16258         if(scrollIntoView !== false){
16259             var el = this.view.getNode(index);
16260             /*
16261              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16262              */
16263             if(el){
16264                 this.list.scrollChildIntoView(el, false);
16265             }
16266         }
16267     },
16268
16269     // private
16270     selectNext : function(){
16271         var ct = this.store.getCount();
16272         if(ct > 0){
16273             if(this.selectedIndex == -1){
16274                 this.select(0);
16275             }else if(this.selectedIndex < ct-1){
16276                 this.select(this.selectedIndex+1);
16277             }
16278         }
16279     },
16280
16281     // private
16282     selectPrev : function(){
16283         var ct = this.store.getCount();
16284         if(ct > 0){
16285             if(this.selectedIndex == -1){
16286                 this.select(0);
16287             }else if(this.selectedIndex != 0){
16288                 this.select(this.selectedIndex-1);
16289             }
16290         }
16291     },
16292
16293     // private
16294     onKeyUp : function(e){
16295         if(this.editable !== false && !e.isSpecialKey()){
16296             this.lastKey = e.getKey();
16297             this.dqTask.delay(this.queryDelay);
16298         }
16299     },
16300
16301     // private
16302     validateBlur : function(){
16303         return !this.list || !this.list.isVisible();   
16304     },
16305
16306     // private
16307     initQuery : function(){
16308         
16309         var v = this.getRawValue();
16310         
16311         if(this.tickable && this.editable){
16312             v = this.tickableInputEl().getValue();
16313         }
16314         
16315         this.doQuery(v);
16316     },
16317
16318     // private
16319     doForce : function(){
16320         if(this.inputEl().dom.value.length > 0){
16321             this.inputEl().dom.value =
16322                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16323              
16324         }
16325     },
16326
16327     /**
16328      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16329      * query allowing the query action to be canceled if needed.
16330      * @param {String} query The SQL query to execute
16331      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16332      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16333      * saved in the current store (defaults to false)
16334      */
16335     doQuery : function(q, forceAll){
16336         
16337         if(q === undefined || q === null){
16338             q = '';
16339         }
16340         var qe = {
16341             query: q,
16342             forceAll: forceAll,
16343             combo: this,
16344             cancel:false
16345         };
16346         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16347             return false;
16348         }
16349         q = qe.query;
16350         
16351         forceAll = qe.forceAll;
16352         if(forceAll === true || (q.length >= this.minChars)){
16353             
16354             this.hasQuery = true;
16355             
16356             if(this.lastQuery != q || this.alwaysQuery){
16357                 this.lastQuery = q;
16358                 if(this.mode == 'local'){
16359                     this.selectedIndex = -1;
16360                     if(forceAll){
16361                         this.store.clearFilter();
16362                     }else{
16363                         
16364                         if(this.specialFilter){
16365                             this.fireEvent('specialfilter', this);
16366                             this.onLoad();
16367                             return;
16368                         }
16369                         
16370                         this.store.filter(this.displayField, q);
16371                     }
16372                     
16373                     this.store.fireEvent("datachanged", this.store);
16374                     
16375                     this.onLoad();
16376                     
16377                     
16378                 }else{
16379                     
16380                     this.store.baseParams[this.queryParam] = q;
16381                     
16382                     var options = {params : this.getParams(q)};
16383                     
16384                     if(this.loadNext){
16385                         options.add = true;
16386                         options.params.start = this.page * this.pageSize;
16387                     }
16388                     
16389                     this.store.load(options);
16390                     
16391                     /*
16392                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16393                      *  we should expand the list on onLoad
16394                      *  so command out it
16395                      */
16396 //                    this.expand();
16397                 }
16398             }else{
16399                 this.selectedIndex = -1;
16400                 this.onLoad();   
16401             }
16402         }
16403         
16404         this.loadNext = false;
16405     },
16406     
16407     // private
16408     getParams : function(q){
16409         var p = {};
16410         //p[this.queryParam] = q;
16411         
16412         if(this.pageSize){
16413             p.start = 0;
16414             p.limit = this.pageSize;
16415         }
16416         return p;
16417     },
16418
16419     /**
16420      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16421      */
16422     collapse : function(){
16423         if(!this.isExpanded()){
16424             return;
16425         }
16426         
16427         this.list.hide();
16428         
16429         this.hasFocus = false;
16430         
16431         if(this.tickable){
16432             this.okBtn.hide();
16433             this.cancelBtn.hide();
16434             this.trigger.show();
16435             
16436             if(this.editable){
16437                 this.tickableInputEl().dom.value = '';
16438                 this.tickableInputEl().blur();
16439             }
16440             
16441         }
16442         
16443         Roo.get(document).un('mousedown', this.collapseIf, this);
16444         Roo.get(document).un('mousewheel', this.collapseIf, this);
16445         if (!this.editable) {
16446             Roo.get(document).un('keydown', this.listKeyPress, this);
16447         }
16448         this.fireEvent('collapse', this);
16449         
16450         this.validate();
16451     },
16452
16453     // private
16454     collapseIf : function(e){
16455         var in_combo  = e.within(this.el);
16456         var in_list =  e.within(this.list);
16457         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16458         
16459         if (in_combo || in_list || is_list) {
16460             //e.stopPropagation();
16461             return;
16462         }
16463         
16464         if(this.tickable){
16465             this.onTickableFooterButtonClick(e, false, false);
16466         }
16467
16468         this.collapse();
16469         
16470     },
16471
16472     /**
16473      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16474      */
16475     expand : function(){
16476        
16477         if(this.isExpanded() || !this.hasFocus){
16478             return;
16479         }
16480         
16481         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16482         this.list.setWidth(lw);
16483         
16484         Roo.log('expand');
16485         
16486         this.list.show();
16487         
16488         this.restrictHeight();
16489         
16490         if(this.tickable){
16491             
16492             this.tickItems = Roo.apply([], this.item);
16493             
16494             this.okBtn.show();
16495             this.cancelBtn.show();
16496             this.trigger.hide();
16497             
16498             if(this.editable){
16499                 this.tickableInputEl().focus();
16500             }
16501             
16502         }
16503         
16504         Roo.get(document).on('mousedown', this.collapseIf, this);
16505         Roo.get(document).on('mousewheel', this.collapseIf, this);
16506         if (!this.editable) {
16507             Roo.get(document).on('keydown', this.listKeyPress, this);
16508         }
16509         
16510         this.fireEvent('expand', this);
16511     },
16512
16513     // private
16514     // Implements the default empty TriggerField.onTriggerClick function
16515     onTriggerClick : function(e)
16516     {
16517         Roo.log('trigger click');
16518         
16519         if(this.disabled || !this.triggerList){
16520             return;
16521         }
16522         
16523         this.page = 0;
16524         this.loadNext = false;
16525         
16526         if(this.isExpanded()){
16527             this.collapse();
16528             if (!this.blockFocus) {
16529                 this.inputEl().focus();
16530             }
16531             
16532         }else {
16533             this.hasFocus = true;
16534             if(this.triggerAction == 'all') {
16535                 this.doQuery(this.allQuery, true);
16536             } else {
16537                 this.doQuery(this.getRawValue());
16538             }
16539             if (!this.blockFocus) {
16540                 this.inputEl().focus();
16541             }
16542         }
16543     },
16544     
16545     onTickableTriggerClick : function(e)
16546     {
16547         if(this.disabled){
16548             return;
16549         }
16550         
16551         this.page = 0;
16552         this.loadNext = false;
16553         this.hasFocus = true;
16554         
16555         if(this.triggerAction == 'all') {
16556             this.doQuery(this.allQuery, true);
16557         } else {
16558             this.doQuery(this.getRawValue());
16559         }
16560     },
16561     
16562     onSearchFieldClick : function(e)
16563     {
16564         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16565             this.onTickableFooterButtonClick(e, false, false);
16566             return;
16567         }
16568         
16569         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16570             return;
16571         }
16572         
16573         this.page = 0;
16574         this.loadNext = false;
16575         this.hasFocus = true;
16576         
16577         if(this.triggerAction == 'all') {
16578             this.doQuery(this.allQuery, true);
16579         } else {
16580             this.doQuery(this.getRawValue());
16581         }
16582     },
16583     
16584     listKeyPress : function(e)
16585     {
16586         //Roo.log('listkeypress');
16587         // scroll to first matching element based on key pres..
16588         if (e.isSpecialKey()) {
16589             return false;
16590         }
16591         var k = String.fromCharCode(e.getKey()).toUpperCase();
16592         //Roo.log(k);
16593         var match  = false;
16594         var csel = this.view.getSelectedNodes();
16595         var cselitem = false;
16596         if (csel.length) {
16597             var ix = this.view.indexOf(csel[0]);
16598             cselitem  = this.store.getAt(ix);
16599             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16600                 cselitem = false;
16601             }
16602             
16603         }
16604         
16605         this.store.each(function(v) { 
16606             if (cselitem) {
16607                 // start at existing selection.
16608                 if (cselitem.id == v.id) {
16609                     cselitem = false;
16610                 }
16611                 return true;
16612             }
16613                 
16614             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16615                 match = this.store.indexOf(v);
16616                 return false;
16617             }
16618             return true;
16619         }, this);
16620         
16621         if (match === false) {
16622             return true; // no more action?
16623         }
16624         // scroll to?
16625         this.view.select(match);
16626         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16627         sn.scrollIntoView(sn.dom.parentNode, false);
16628     },
16629     
16630     onViewScroll : function(e, t){
16631         
16632         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){
16633             return;
16634         }
16635         
16636         this.hasQuery = true;
16637         
16638         this.loading = this.list.select('.loading', true).first();
16639         
16640         if(this.loading === null){
16641             this.list.createChild({
16642                 tag: 'div',
16643                 cls: 'loading roo-select2-more-results roo-select2-active',
16644                 html: 'Loading more results...'
16645             });
16646             
16647             this.loading = this.list.select('.loading', true).first();
16648             
16649             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16650             
16651             this.loading.hide();
16652         }
16653         
16654         this.loading.show();
16655         
16656         var _combo = this;
16657         
16658         this.page++;
16659         this.loadNext = true;
16660         
16661         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16662         
16663         return;
16664     },
16665     
16666     addItem : function(o)
16667     {   
16668         var dv = ''; // display value
16669         
16670         if (this.displayField) {
16671             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16672         } else {
16673             // this is an error condition!!!
16674             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16675         }
16676         
16677         if(!dv.length){
16678             return;
16679         }
16680         
16681         var choice = this.choices.createChild({
16682             tag: 'li',
16683             cls: 'roo-select2-search-choice',
16684             cn: [
16685                 {
16686                     tag: 'div',
16687                     html: dv
16688                 },
16689                 {
16690                     tag: 'a',
16691                     href: '#',
16692                     cls: 'roo-select2-search-choice-close fa fa-times',
16693                     tabindex: '-1'
16694                 }
16695             ]
16696             
16697         }, this.searchField);
16698         
16699         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16700         
16701         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16702         
16703         this.item.push(o);
16704         
16705         this.lastData = o;
16706         
16707         this.syncValue();
16708         
16709         this.inputEl().dom.value = '';
16710         
16711         this.validate();
16712     },
16713     
16714     onRemoveItem : function(e, _self, o)
16715     {
16716         e.preventDefault();
16717         
16718         this.lastItem = Roo.apply([], this.item);
16719         
16720         var index = this.item.indexOf(o.data) * 1;
16721         
16722         if( index < 0){
16723             Roo.log('not this item?!');
16724             return;
16725         }
16726         
16727         this.item.splice(index, 1);
16728         o.item.remove();
16729         
16730         this.syncValue();
16731         
16732         this.fireEvent('remove', this, e);
16733         
16734         this.validate();
16735         
16736     },
16737     
16738     syncValue : function()
16739     {
16740         if(!this.item.length){
16741             this.clearValue();
16742             return;
16743         }
16744             
16745         var value = [];
16746         var _this = this;
16747         Roo.each(this.item, function(i){
16748             if(_this.valueField){
16749                 value.push(i[_this.valueField]);
16750                 return;
16751             }
16752
16753             value.push(i);
16754         });
16755
16756         this.value = value.join(',');
16757
16758         if(this.hiddenField){
16759             this.hiddenField.dom.value = this.value;
16760         }
16761         
16762         this.store.fireEvent("datachanged", this.store);
16763         
16764         this.validate();
16765     },
16766     
16767     clearItem : function()
16768     {
16769         if(!this.multiple){
16770             return;
16771         }
16772         
16773         this.item = [];
16774         
16775         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16776            c.remove();
16777         });
16778         
16779         this.syncValue();
16780         
16781         this.validate();
16782         
16783         if(this.tickable && !Roo.isTouch){
16784             this.view.refresh();
16785         }
16786     },
16787     
16788     inputEl: function ()
16789     {
16790         if(Roo.isIOS && this.useNativeIOS){
16791             return this.el.select('select.roo-ios-select', true).first();
16792         }
16793         
16794         if(Roo.isTouch && this.mobileTouchView){
16795             return this.el.select('input.form-control',true).first();
16796         }
16797         
16798         if(this.tickable){
16799             return this.searchField;
16800         }
16801         
16802         return this.el.select('input.form-control',true).first();
16803     },
16804     
16805     onTickableFooterButtonClick : function(e, btn, el)
16806     {
16807         e.preventDefault();
16808         
16809         this.lastItem = Roo.apply([], this.item);
16810         
16811         if(btn && btn.name == 'cancel'){
16812             this.tickItems = Roo.apply([], this.item);
16813             this.collapse();
16814             return;
16815         }
16816         
16817         this.clearItem();
16818         
16819         var _this = this;
16820         
16821         Roo.each(this.tickItems, function(o){
16822             _this.addItem(o);
16823         });
16824         
16825         this.collapse();
16826         
16827     },
16828     
16829     validate : function()
16830     {
16831         if(this.getVisibilityEl().hasClass('hidden')){
16832             return true;
16833         }
16834         
16835         var v = this.getRawValue();
16836         
16837         if(this.multiple){
16838             v = this.getValue();
16839         }
16840         
16841         if(this.disabled || this.allowBlank || v.length){
16842             this.markValid();
16843             return true;
16844         }
16845         
16846         this.markInvalid();
16847         return false;
16848     },
16849     
16850     tickableInputEl : function()
16851     {
16852         if(!this.tickable || !this.editable){
16853             return this.inputEl();
16854         }
16855         
16856         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16857     },
16858     
16859     
16860     getAutoCreateTouchView : function()
16861     {
16862         var id = Roo.id();
16863         
16864         var cfg = {
16865             cls: 'form-group' //input-group
16866         };
16867         
16868         var input =  {
16869             tag: 'input',
16870             id : id,
16871             type : this.inputType,
16872             cls : 'form-control x-combo-noedit',
16873             autocomplete: 'new-password',
16874             placeholder : this.placeholder || '',
16875             readonly : true
16876         };
16877         
16878         if (this.name) {
16879             input.name = this.name;
16880         }
16881         
16882         if (this.size) {
16883             input.cls += ' input-' + this.size;
16884         }
16885         
16886         if (this.disabled) {
16887             input.disabled = true;
16888         }
16889         
16890         var inputblock = {
16891             cls : '',
16892             cn : [
16893                 input
16894             ]
16895         };
16896         
16897         if(this.before){
16898             inputblock.cls += ' input-group';
16899             
16900             inputblock.cn.unshift({
16901                 tag :'span',
16902                 cls : 'input-group-addon input-group-prepend input-group-text',
16903                 html : this.before
16904             });
16905         }
16906         
16907         if(this.removable && !this.multiple){
16908             inputblock.cls += ' roo-removable';
16909             
16910             inputblock.cn.push({
16911                 tag: 'button',
16912                 html : 'x',
16913                 cls : 'roo-combo-removable-btn close'
16914             });
16915         }
16916
16917         if(this.hasFeedback && !this.allowBlank){
16918             
16919             inputblock.cls += ' has-feedback';
16920             
16921             inputblock.cn.push({
16922                 tag: 'span',
16923                 cls: 'glyphicon form-control-feedback'
16924             });
16925             
16926         }
16927         
16928         if (this.after) {
16929             
16930             inputblock.cls += (this.before) ? '' : ' input-group';
16931             
16932             inputblock.cn.push({
16933                 tag :'span',
16934                 cls : 'input-group-addon input-group-append input-group-text',
16935                 html : this.after
16936             });
16937         }
16938
16939         
16940         var ibwrap = inputblock;
16941         
16942         if(this.multiple){
16943             ibwrap = {
16944                 tag: 'ul',
16945                 cls: 'roo-select2-choices',
16946                 cn:[
16947                     {
16948                         tag: 'li',
16949                         cls: 'roo-select2-search-field',
16950                         cn: [
16951
16952                             inputblock
16953                         ]
16954                     }
16955                 ]
16956             };
16957         
16958             
16959         }
16960         
16961         var combobox = {
16962             cls: 'roo-select2-container input-group roo-touchview-combobox ',
16963             cn: [
16964                 {
16965                     tag: 'input',
16966                     type : 'hidden',
16967                     cls: 'form-hidden-field'
16968                 },
16969                 ibwrap
16970             ]
16971         };
16972         
16973         if(!this.multiple && this.showToggleBtn){
16974             
16975             var caret = {
16976                 cls: 'caret'
16977             };
16978             
16979             if (this.caret != false) {
16980                 caret = {
16981                      tag: 'i',
16982                      cls: 'fa fa-' + this.caret
16983                 };
16984                 
16985             }
16986             
16987             combobox.cn.push({
16988                 tag :'span',
16989                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
16990                 cn : [
16991                     Roo.bootstrap.version == 3 ? caret : '',
16992                     {
16993                         tag: 'span',
16994                         cls: 'combobox-clear',
16995                         cn  : [
16996                             {
16997                                 tag : 'i',
16998                                 cls: 'icon-remove'
16999                             }
17000                         ]
17001                     }
17002                 ]
17003
17004             })
17005         }
17006         
17007         if(this.multiple){
17008             combobox.cls += ' roo-select2-container-multi';
17009         }
17010         
17011         var align = this.labelAlign || this.parentLabelAlign();
17012         
17013         if (align ==='left' && this.fieldLabel.length) {
17014
17015             cfg.cn = [
17016                 {
17017                    tag : 'i',
17018                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17019                    tooltip : 'This field is required'
17020                 },
17021                 {
17022                     tag: 'label',
17023                     cls : 'control-label col-form-label',
17024                     html : this.fieldLabel
17025
17026                 },
17027                 {
17028                     cls : '', 
17029                     cn: [
17030                         combobox
17031                     ]
17032                 }
17033             ];
17034             
17035             var labelCfg = cfg.cn[1];
17036             var contentCfg = cfg.cn[2];
17037             
17038
17039             if(this.indicatorpos == 'right'){
17040                 cfg.cn = [
17041                     {
17042                         tag: 'label',
17043                         'for' :  id,
17044                         cls : 'control-label col-form-label',
17045                         cn : [
17046                             {
17047                                 tag : 'span',
17048                                 html : this.fieldLabel
17049                             },
17050                             {
17051                                 tag : 'i',
17052                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17053                                 tooltip : 'This field is required'
17054                             }
17055                         ]
17056                     },
17057                     {
17058                         cls : "",
17059                         cn: [
17060                             combobox
17061                         ]
17062                     }
17063
17064                 ];
17065                 
17066                 labelCfg = cfg.cn[0];
17067                 contentCfg = cfg.cn[1];
17068             }
17069             
17070            
17071             
17072             if(this.labelWidth > 12){
17073                 labelCfg.style = "width: " + this.labelWidth + 'px';
17074             }
17075             
17076             if(this.labelWidth < 13 && this.labelmd == 0){
17077                 this.labelmd = this.labelWidth;
17078             }
17079             
17080             if(this.labellg > 0){
17081                 labelCfg.cls += ' col-lg-' + this.labellg;
17082                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17083             }
17084             
17085             if(this.labelmd > 0){
17086                 labelCfg.cls += ' col-md-' + this.labelmd;
17087                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17088             }
17089             
17090             if(this.labelsm > 0){
17091                 labelCfg.cls += ' col-sm-' + this.labelsm;
17092                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17093             }
17094             
17095             if(this.labelxs > 0){
17096                 labelCfg.cls += ' col-xs-' + this.labelxs;
17097                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17098             }
17099                 
17100                 
17101         } else if ( this.fieldLabel.length) {
17102             cfg.cn = [
17103                 {
17104                    tag : 'i',
17105                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17106                    tooltip : 'This field is required'
17107                 },
17108                 {
17109                     tag: 'label',
17110                     cls : 'control-label',
17111                     html : this.fieldLabel
17112
17113                 },
17114                 {
17115                     cls : '', 
17116                     cn: [
17117                         combobox
17118                     ]
17119                 }
17120             ];
17121             
17122             if(this.indicatorpos == 'right'){
17123                 cfg.cn = [
17124                     {
17125                         tag: 'label',
17126                         cls : 'control-label',
17127                         html : this.fieldLabel,
17128                         cn : [
17129                             {
17130                                tag : 'i',
17131                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17132                                tooltip : 'This field is required'
17133                             }
17134                         ]
17135                     },
17136                     {
17137                         cls : '', 
17138                         cn: [
17139                             combobox
17140                         ]
17141                     }
17142                 ];
17143             }
17144         } else {
17145             cfg.cn = combobox;    
17146         }
17147         
17148         
17149         var settings = this;
17150         
17151         ['xs','sm','md','lg'].map(function(size){
17152             if (settings[size]) {
17153                 cfg.cls += ' col-' + size + '-' + settings[size];
17154             }
17155         });
17156         
17157         return cfg;
17158     },
17159     
17160     initTouchView : function()
17161     {
17162         this.renderTouchView();
17163         
17164         this.touchViewEl.on('scroll', function(){
17165             this.el.dom.scrollTop = 0;
17166         }, this);
17167         
17168         this.originalValue = this.getValue();
17169         
17170         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17171         
17172         this.inputEl().on("click", this.showTouchView, this);
17173         if (this.triggerEl) {
17174             this.triggerEl.on("click", this.showTouchView, this);
17175         }
17176         
17177         
17178         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17179         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17180         
17181         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17182         
17183         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17184         this.store.on('load', this.onTouchViewLoad, this);
17185         this.store.on('loadexception', this.onTouchViewLoadException, this);
17186         
17187         if(this.hiddenName){
17188             
17189             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17190             
17191             this.hiddenField.dom.value =
17192                 this.hiddenValue !== undefined ? this.hiddenValue :
17193                 this.value !== undefined ? this.value : '';
17194         
17195             this.el.dom.removeAttribute('name');
17196             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17197         }
17198         
17199         if(this.multiple){
17200             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17201             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17202         }
17203         
17204         if(this.removable && !this.multiple){
17205             var close = this.closeTriggerEl();
17206             if(close){
17207                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17208                 close.on('click', this.removeBtnClick, this, close);
17209             }
17210         }
17211         /*
17212          * fix the bug in Safari iOS8
17213          */
17214         this.inputEl().on("focus", function(e){
17215             document.activeElement.blur();
17216         }, this);
17217         
17218         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17219         
17220         return;
17221         
17222         
17223     },
17224     
17225     renderTouchView : function()
17226     {
17227         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17228         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17229         
17230         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17231         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17232         
17233         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17234         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17235         this.touchViewBodyEl.setStyle('overflow', 'auto');
17236         
17237         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17238         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17239         
17240         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17241         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17242         
17243     },
17244     
17245     showTouchView : function()
17246     {
17247         if(this.disabled){
17248             return;
17249         }
17250         
17251         this.touchViewHeaderEl.hide();
17252
17253         if(this.modalTitle.length){
17254             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17255             this.touchViewHeaderEl.show();
17256         }
17257
17258         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17259         this.touchViewEl.show();
17260
17261         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17262         
17263         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17264         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17265
17266         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17267
17268         if(this.modalTitle.length){
17269             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17270         }
17271         
17272         this.touchViewBodyEl.setHeight(bodyHeight);
17273
17274         if(this.animate){
17275             var _this = this;
17276             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
17277         }else{
17278             this.touchViewEl.addClass('in');
17279         }
17280         
17281         if(this._touchViewMask){
17282             Roo.get(document.body).addClass("x-body-masked");
17283             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17284             this._touchViewMask.setStyle('z-index', 10000);
17285             this._touchViewMask.addClass('show');
17286         }
17287         
17288         this.doTouchViewQuery();
17289         
17290     },
17291     
17292     hideTouchView : function()
17293     {
17294         this.touchViewEl.removeClass('in');
17295
17296         if(this.animate){
17297             var _this = this;
17298             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17299         }else{
17300             this.touchViewEl.setStyle('display', 'none');
17301         }
17302         
17303         if(this._touchViewMask){
17304             this._touchViewMask.removeClass('show');
17305             Roo.get(document.body).removeClass("x-body-masked");
17306         }
17307     },
17308     
17309     setTouchViewValue : function()
17310     {
17311         if(this.multiple){
17312             this.clearItem();
17313         
17314             var _this = this;
17315
17316             Roo.each(this.tickItems, function(o){
17317                 this.addItem(o);
17318             }, this);
17319         }
17320         
17321         this.hideTouchView();
17322     },
17323     
17324     doTouchViewQuery : function()
17325     {
17326         var qe = {
17327             query: '',
17328             forceAll: true,
17329             combo: this,
17330             cancel:false
17331         };
17332         
17333         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17334             return false;
17335         }
17336         
17337         if(!this.alwaysQuery || this.mode == 'local'){
17338             this.onTouchViewLoad();
17339             return;
17340         }
17341         
17342         this.store.load();
17343     },
17344     
17345     onTouchViewBeforeLoad : function(combo,opts)
17346     {
17347         return;
17348     },
17349
17350     // private
17351     onTouchViewLoad : function()
17352     {
17353         if(this.store.getCount() < 1){
17354             this.onTouchViewEmptyResults();
17355             return;
17356         }
17357         
17358         this.clearTouchView();
17359         
17360         var rawValue = this.getRawValue();
17361         
17362         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17363         
17364         this.tickItems = [];
17365         
17366         this.store.data.each(function(d, rowIndex){
17367             var row = this.touchViewListGroup.createChild(template);
17368             
17369             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17370                 row.addClass(d.data.cls);
17371             }
17372             
17373             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17374                 var cfg = {
17375                     data : d.data,
17376                     html : d.data[this.displayField]
17377                 };
17378                 
17379                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17380                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17381                 }
17382             }
17383             row.removeClass('selected');
17384             if(!this.multiple && this.valueField &&
17385                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17386             {
17387                 // radio buttons..
17388                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17389                 row.addClass('selected');
17390             }
17391             
17392             if(this.multiple && this.valueField &&
17393                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17394             {
17395                 
17396                 // checkboxes...
17397                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17398                 this.tickItems.push(d.data);
17399             }
17400             
17401             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17402             
17403         }, this);
17404         
17405         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17406         
17407         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17408
17409         if(this.modalTitle.length){
17410             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17411         }
17412
17413         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17414         
17415         if(this.mobile_restrict_height && listHeight < bodyHeight){
17416             this.touchViewBodyEl.setHeight(listHeight);
17417         }
17418         
17419         var _this = this;
17420         
17421         if(firstChecked && listHeight > bodyHeight){
17422             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17423         }
17424         
17425     },
17426     
17427     onTouchViewLoadException : function()
17428     {
17429         this.hideTouchView();
17430     },
17431     
17432     onTouchViewEmptyResults : function()
17433     {
17434         this.clearTouchView();
17435         
17436         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17437         
17438         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17439         
17440     },
17441     
17442     clearTouchView : function()
17443     {
17444         this.touchViewListGroup.dom.innerHTML = '';
17445     },
17446     
17447     onTouchViewClick : function(e, el, o)
17448     {
17449         e.preventDefault();
17450         
17451         var row = o.row;
17452         var rowIndex = o.rowIndex;
17453         
17454         var r = this.store.getAt(rowIndex);
17455         
17456         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17457             
17458             if(!this.multiple){
17459                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17460                     c.dom.removeAttribute('checked');
17461                 }, this);
17462
17463                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17464
17465                 this.setFromData(r.data);
17466
17467                 var close = this.closeTriggerEl();
17468
17469                 if(close){
17470                     close.show();
17471                 }
17472
17473                 this.hideTouchView();
17474
17475                 this.fireEvent('select', this, r, rowIndex);
17476
17477                 return;
17478             }
17479
17480             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17481                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17482                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17483                 return;
17484             }
17485
17486             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17487             this.addItem(r.data);
17488             this.tickItems.push(r.data);
17489         }
17490     },
17491     
17492     getAutoCreateNativeIOS : function()
17493     {
17494         var cfg = {
17495             cls: 'form-group' //input-group,
17496         };
17497         
17498         var combobox =  {
17499             tag: 'select',
17500             cls : 'roo-ios-select'
17501         };
17502         
17503         if (this.name) {
17504             combobox.name = this.name;
17505         }
17506         
17507         if (this.disabled) {
17508             combobox.disabled = true;
17509         }
17510         
17511         var settings = this;
17512         
17513         ['xs','sm','md','lg'].map(function(size){
17514             if (settings[size]) {
17515                 cfg.cls += ' col-' + size + '-' + settings[size];
17516             }
17517         });
17518         
17519         cfg.cn = combobox;
17520         
17521         return cfg;
17522         
17523     },
17524     
17525     initIOSView : function()
17526     {
17527         this.store.on('load', this.onIOSViewLoad, this);
17528         
17529         return;
17530     },
17531     
17532     onIOSViewLoad : function()
17533     {
17534         if(this.store.getCount() < 1){
17535             return;
17536         }
17537         
17538         this.clearIOSView();
17539         
17540         if(this.allowBlank) {
17541             
17542             var default_text = '-- SELECT --';
17543             
17544             if(this.placeholder.length){
17545                 default_text = this.placeholder;
17546             }
17547             
17548             if(this.emptyTitle.length){
17549                 default_text += ' - ' + this.emptyTitle + ' -';
17550             }
17551             
17552             var opt = this.inputEl().createChild({
17553                 tag: 'option',
17554                 value : 0,
17555                 html : default_text
17556             });
17557             
17558             var o = {};
17559             o[this.valueField] = 0;
17560             o[this.displayField] = default_text;
17561             
17562             this.ios_options.push({
17563                 data : o,
17564                 el : opt
17565             });
17566             
17567         }
17568         
17569         this.store.data.each(function(d, rowIndex){
17570             
17571             var html = '';
17572             
17573             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17574                 html = d.data[this.displayField];
17575             }
17576             
17577             var value = '';
17578             
17579             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17580                 value = d.data[this.valueField];
17581             }
17582             
17583             var option = {
17584                 tag: 'option',
17585                 value : value,
17586                 html : html
17587             };
17588             
17589             if(this.value == d.data[this.valueField]){
17590                 option['selected'] = true;
17591             }
17592             
17593             var opt = this.inputEl().createChild(option);
17594             
17595             this.ios_options.push({
17596                 data : d.data,
17597                 el : opt
17598             });
17599             
17600         }, this);
17601         
17602         this.inputEl().on('change', function(){
17603            this.fireEvent('select', this);
17604         }, this);
17605         
17606     },
17607     
17608     clearIOSView: function()
17609     {
17610         this.inputEl().dom.innerHTML = '';
17611         
17612         this.ios_options = [];
17613     },
17614     
17615     setIOSValue: function(v)
17616     {
17617         this.value = v;
17618         
17619         if(!this.ios_options){
17620             return;
17621         }
17622         
17623         Roo.each(this.ios_options, function(opts){
17624            
17625            opts.el.dom.removeAttribute('selected');
17626            
17627            if(opts.data[this.valueField] != v){
17628                return;
17629            }
17630            
17631            opts.el.dom.setAttribute('selected', true);
17632            
17633         }, this);
17634     }
17635
17636     /** 
17637     * @cfg {Boolean} grow 
17638     * @hide 
17639     */
17640     /** 
17641     * @cfg {Number} growMin 
17642     * @hide 
17643     */
17644     /** 
17645     * @cfg {Number} growMax 
17646     * @hide 
17647     */
17648     /**
17649      * @hide
17650      * @method autoSize
17651      */
17652 });
17653
17654 Roo.apply(Roo.bootstrap.ComboBox,  {
17655     
17656     header : {
17657         tag: 'div',
17658         cls: 'modal-header',
17659         cn: [
17660             {
17661                 tag: 'h4',
17662                 cls: 'modal-title'
17663             }
17664         ]
17665     },
17666     
17667     body : {
17668         tag: 'div',
17669         cls: 'modal-body',
17670         cn: [
17671             {
17672                 tag: 'ul',
17673                 cls: 'list-group'
17674             }
17675         ]
17676     },
17677     
17678     listItemRadio : {
17679         tag: 'li',
17680         cls: 'list-group-item',
17681         cn: [
17682             {
17683                 tag: 'span',
17684                 cls: 'roo-combobox-list-group-item-value'
17685             },
17686             {
17687                 tag: 'div',
17688                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17689                 cn: [
17690                     {
17691                         tag: 'input',
17692                         type: 'radio'
17693                     },
17694                     {
17695                         tag: 'label'
17696                     }
17697                 ]
17698             }
17699         ]
17700     },
17701     
17702     listItemCheckbox : {
17703         tag: 'li',
17704         cls: 'list-group-item',
17705         cn: [
17706             {
17707                 tag: 'span',
17708                 cls: 'roo-combobox-list-group-item-value'
17709             },
17710             {
17711                 tag: 'div',
17712                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17713                 cn: [
17714                     {
17715                         tag: 'input',
17716                         type: 'checkbox'
17717                     },
17718                     {
17719                         tag: 'label'
17720                     }
17721                 ]
17722             }
17723         ]
17724     },
17725     
17726     emptyResult : {
17727         tag: 'div',
17728         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17729     },
17730     
17731     footer : {
17732         tag: 'div',
17733         cls: 'modal-footer',
17734         cn: [
17735             {
17736                 tag: 'div',
17737                 cls: 'row',
17738                 cn: [
17739                     {
17740                         tag: 'div',
17741                         cls: 'col-xs-6 text-left',
17742                         cn: {
17743                             tag: 'button',
17744                             cls: 'btn btn-danger roo-touch-view-cancel',
17745                             html: 'Cancel'
17746                         }
17747                     },
17748                     {
17749                         tag: 'div',
17750                         cls: 'col-xs-6 text-right',
17751                         cn: {
17752                             tag: 'button',
17753                             cls: 'btn btn-success roo-touch-view-ok',
17754                             html: 'OK'
17755                         }
17756                     }
17757                 ]
17758             }
17759         ]
17760         
17761     }
17762 });
17763
17764 Roo.apply(Roo.bootstrap.ComboBox,  {
17765     
17766     touchViewTemplate : {
17767         tag: 'div',
17768         cls: 'modal fade roo-combobox-touch-view',
17769         cn: [
17770             {
17771                 tag: 'div',
17772                 cls: 'modal-dialog',
17773                 style : 'position:fixed', // we have to fix position....
17774                 cn: [
17775                     {
17776                         tag: 'div',
17777                         cls: 'modal-content',
17778                         cn: [
17779                             Roo.bootstrap.ComboBox.header,
17780                             Roo.bootstrap.ComboBox.body,
17781                             Roo.bootstrap.ComboBox.footer
17782                         ]
17783                     }
17784                 ]
17785             }
17786         ]
17787     }
17788 });/*
17789  * Based on:
17790  * Ext JS Library 1.1.1
17791  * Copyright(c) 2006-2007, Ext JS, LLC.
17792  *
17793  * Originally Released Under LGPL - original licence link has changed is not relivant.
17794  *
17795  * Fork - LGPL
17796  * <script type="text/javascript">
17797  */
17798
17799 /**
17800  * @class Roo.View
17801  * @extends Roo.util.Observable
17802  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17803  * This class also supports single and multi selection modes. <br>
17804  * Create a data model bound view:
17805  <pre><code>
17806  var store = new Roo.data.Store(...);
17807
17808  var view = new Roo.View({
17809     el : "my-element",
17810     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17811  
17812     singleSelect: true,
17813     selectedClass: "ydataview-selected",
17814     store: store
17815  });
17816
17817  // listen for node click?
17818  view.on("click", function(vw, index, node, e){
17819  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17820  });
17821
17822  // load XML data
17823  dataModel.load("foobar.xml");
17824  </code></pre>
17825  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17826  * <br><br>
17827  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17828  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17829  * 
17830  * Note: old style constructor is still suported (container, template, config)
17831  * 
17832  * @constructor
17833  * Create a new View
17834  * @param {Object} config The config object
17835  * 
17836  */
17837 Roo.View = function(config, depreciated_tpl, depreciated_config){
17838     
17839     this.parent = false;
17840     
17841     if (typeof(depreciated_tpl) == 'undefined') {
17842         // new way.. - universal constructor.
17843         Roo.apply(this, config);
17844         this.el  = Roo.get(this.el);
17845     } else {
17846         // old format..
17847         this.el  = Roo.get(config);
17848         this.tpl = depreciated_tpl;
17849         Roo.apply(this, depreciated_config);
17850     }
17851     this.wrapEl  = this.el.wrap().wrap();
17852     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17853     
17854     
17855     if(typeof(this.tpl) == "string"){
17856         this.tpl = new Roo.Template(this.tpl);
17857     } else {
17858         // support xtype ctors..
17859         this.tpl = new Roo.factory(this.tpl, Roo);
17860     }
17861     
17862     
17863     this.tpl.compile();
17864     
17865     /** @private */
17866     this.addEvents({
17867         /**
17868          * @event beforeclick
17869          * Fires before a click is processed. Returns false to cancel the default action.
17870          * @param {Roo.View} this
17871          * @param {Number} index The index of the target node
17872          * @param {HTMLElement} node The target node
17873          * @param {Roo.EventObject} e The raw event object
17874          */
17875             "beforeclick" : true,
17876         /**
17877          * @event click
17878          * Fires when a template node is clicked.
17879          * @param {Roo.View} this
17880          * @param {Number} index The index of the target node
17881          * @param {HTMLElement} node The target node
17882          * @param {Roo.EventObject} e The raw event object
17883          */
17884             "click" : true,
17885         /**
17886          * @event dblclick
17887          * Fires when a template node is double clicked.
17888          * @param {Roo.View} this
17889          * @param {Number} index The index of the target node
17890          * @param {HTMLElement} node The target node
17891          * @param {Roo.EventObject} e The raw event object
17892          */
17893             "dblclick" : true,
17894         /**
17895          * @event contextmenu
17896          * Fires when a template node is right clicked.
17897          * @param {Roo.View} this
17898          * @param {Number} index The index of the target node
17899          * @param {HTMLElement} node The target node
17900          * @param {Roo.EventObject} e The raw event object
17901          */
17902             "contextmenu" : true,
17903         /**
17904          * @event selectionchange
17905          * Fires when the selected nodes change.
17906          * @param {Roo.View} this
17907          * @param {Array} selections Array of the selected nodes
17908          */
17909             "selectionchange" : true,
17910     
17911         /**
17912          * @event beforeselect
17913          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17914          * @param {Roo.View} this
17915          * @param {HTMLElement} node The node to be selected
17916          * @param {Array} selections Array of currently selected nodes
17917          */
17918             "beforeselect" : true,
17919         /**
17920          * @event preparedata
17921          * Fires on every row to render, to allow you to change the data.
17922          * @param {Roo.View} this
17923          * @param {Object} data to be rendered (change this)
17924          */
17925           "preparedata" : true
17926           
17927           
17928         });
17929
17930
17931
17932     this.el.on({
17933         "click": this.onClick,
17934         "dblclick": this.onDblClick,
17935         "contextmenu": this.onContextMenu,
17936         scope:this
17937     });
17938
17939     this.selections = [];
17940     this.nodes = [];
17941     this.cmp = new Roo.CompositeElementLite([]);
17942     if(this.store){
17943         this.store = Roo.factory(this.store, Roo.data);
17944         this.setStore(this.store, true);
17945     }
17946     
17947     if ( this.footer && this.footer.xtype) {
17948            
17949          var fctr = this.wrapEl.appendChild(document.createElement("div"));
17950         
17951         this.footer.dataSource = this.store;
17952         this.footer.container = fctr;
17953         this.footer = Roo.factory(this.footer, Roo);
17954         fctr.insertFirst(this.el);
17955         
17956         // this is a bit insane - as the paging toolbar seems to detach the el..
17957 //        dom.parentNode.parentNode.parentNode
17958          // they get detached?
17959     }
17960     
17961     
17962     Roo.View.superclass.constructor.call(this);
17963     
17964     
17965 };
17966
17967 Roo.extend(Roo.View, Roo.util.Observable, {
17968     
17969      /**
17970      * @cfg {Roo.data.Store} store Data store to load data from.
17971      */
17972     store : false,
17973     
17974     /**
17975      * @cfg {String|Roo.Element} el The container element.
17976      */
17977     el : '',
17978     
17979     /**
17980      * @cfg {String|Roo.Template} tpl The template used by this View 
17981      */
17982     tpl : false,
17983     /**
17984      * @cfg {String} dataName the named area of the template to use as the data area
17985      *                          Works with domtemplates roo-name="name"
17986      */
17987     dataName: false,
17988     /**
17989      * @cfg {String} selectedClass The css class to add to selected nodes
17990      */
17991     selectedClass : "x-view-selected",
17992      /**
17993      * @cfg {String} emptyText The empty text to show when nothing is loaded.
17994      */
17995     emptyText : "",
17996     
17997     /**
17998      * @cfg {String} text to display on mask (default Loading)
17999      */
18000     mask : false,
18001     /**
18002      * @cfg {Boolean} multiSelect Allow multiple selection
18003      */
18004     multiSelect : false,
18005     /**
18006      * @cfg {Boolean} singleSelect Allow single selection
18007      */
18008     singleSelect:  false,
18009     
18010     /**
18011      * @cfg {Boolean} toggleSelect - selecting 
18012      */
18013     toggleSelect : false,
18014     
18015     /**
18016      * @cfg {Boolean} tickable - selecting 
18017      */
18018     tickable : false,
18019     
18020     /**
18021      * Returns the element this view is bound to.
18022      * @return {Roo.Element}
18023      */
18024     getEl : function(){
18025         return this.wrapEl;
18026     },
18027     
18028     
18029
18030     /**
18031      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18032      */
18033     refresh : function(){
18034         //Roo.log('refresh');
18035         var t = this.tpl;
18036         
18037         // if we are using something like 'domtemplate', then
18038         // the what gets used is:
18039         // t.applySubtemplate(NAME, data, wrapping data..)
18040         // the outer template then get' applied with
18041         //     the store 'extra data'
18042         // and the body get's added to the
18043         //      roo-name="data" node?
18044         //      <span class='roo-tpl-{name}'></span> ?????
18045         
18046         
18047         
18048         this.clearSelections();
18049         this.el.update("");
18050         var html = [];
18051         var records = this.store.getRange();
18052         if(records.length < 1) {
18053             
18054             // is this valid??  = should it render a template??
18055             
18056             this.el.update(this.emptyText);
18057             return;
18058         }
18059         var el = this.el;
18060         if (this.dataName) {
18061             this.el.update(t.apply(this.store.meta)); //????
18062             el = this.el.child('.roo-tpl-' + this.dataName);
18063         }
18064         
18065         for(var i = 0, len = records.length; i < len; i++){
18066             var data = this.prepareData(records[i].data, i, records[i]);
18067             this.fireEvent("preparedata", this, data, i, records[i]);
18068             
18069             var d = Roo.apply({}, data);
18070             
18071             if(this.tickable){
18072                 Roo.apply(d, {'roo-id' : Roo.id()});
18073                 
18074                 var _this = this;
18075             
18076                 Roo.each(this.parent.item, function(item){
18077                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18078                         return;
18079                     }
18080                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18081                 });
18082             }
18083             
18084             html[html.length] = Roo.util.Format.trim(
18085                 this.dataName ?
18086                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18087                     t.apply(d)
18088             );
18089         }
18090         
18091         
18092         
18093         el.update(html.join(""));
18094         this.nodes = el.dom.childNodes;
18095         this.updateIndexes(0);
18096     },
18097     
18098
18099     /**
18100      * Function to override to reformat the data that is sent to
18101      * the template for each node.
18102      * DEPRICATED - use the preparedata event handler.
18103      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18104      * a JSON object for an UpdateManager bound view).
18105      */
18106     prepareData : function(data, index, record)
18107     {
18108         this.fireEvent("preparedata", this, data, index, record);
18109         return data;
18110     },
18111
18112     onUpdate : function(ds, record){
18113         // Roo.log('on update');   
18114         this.clearSelections();
18115         var index = this.store.indexOf(record);
18116         var n = this.nodes[index];
18117         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18118         n.parentNode.removeChild(n);
18119         this.updateIndexes(index, index);
18120     },
18121
18122     
18123     
18124 // --------- FIXME     
18125     onAdd : function(ds, records, index)
18126     {
18127         //Roo.log(['on Add', ds, records, index] );        
18128         this.clearSelections();
18129         if(this.nodes.length == 0){
18130             this.refresh();
18131             return;
18132         }
18133         var n = this.nodes[index];
18134         for(var i = 0, len = records.length; i < len; i++){
18135             var d = this.prepareData(records[i].data, i, records[i]);
18136             if(n){
18137                 this.tpl.insertBefore(n, d);
18138             }else{
18139                 
18140                 this.tpl.append(this.el, d);
18141             }
18142         }
18143         this.updateIndexes(index);
18144     },
18145
18146     onRemove : function(ds, record, index){
18147        // Roo.log('onRemove');
18148         this.clearSelections();
18149         var el = this.dataName  ?
18150             this.el.child('.roo-tpl-' + this.dataName) :
18151             this.el; 
18152         
18153         el.dom.removeChild(this.nodes[index]);
18154         this.updateIndexes(index);
18155     },
18156
18157     /**
18158      * Refresh an individual node.
18159      * @param {Number} index
18160      */
18161     refreshNode : function(index){
18162         this.onUpdate(this.store, this.store.getAt(index));
18163     },
18164
18165     updateIndexes : function(startIndex, endIndex){
18166         var ns = this.nodes;
18167         startIndex = startIndex || 0;
18168         endIndex = endIndex || ns.length - 1;
18169         for(var i = startIndex; i <= endIndex; i++){
18170             ns[i].nodeIndex = i;
18171         }
18172     },
18173
18174     /**
18175      * Changes the data store this view uses and refresh the view.
18176      * @param {Store} store
18177      */
18178     setStore : function(store, initial){
18179         if(!initial && this.store){
18180             this.store.un("datachanged", this.refresh);
18181             this.store.un("add", this.onAdd);
18182             this.store.un("remove", this.onRemove);
18183             this.store.un("update", this.onUpdate);
18184             this.store.un("clear", this.refresh);
18185             this.store.un("beforeload", this.onBeforeLoad);
18186             this.store.un("load", this.onLoad);
18187             this.store.un("loadexception", this.onLoad);
18188         }
18189         if(store){
18190           
18191             store.on("datachanged", this.refresh, this);
18192             store.on("add", this.onAdd, this);
18193             store.on("remove", this.onRemove, this);
18194             store.on("update", this.onUpdate, this);
18195             store.on("clear", this.refresh, this);
18196             store.on("beforeload", this.onBeforeLoad, this);
18197             store.on("load", this.onLoad, this);
18198             store.on("loadexception", this.onLoad, this);
18199         }
18200         
18201         if(store){
18202             this.refresh();
18203         }
18204     },
18205     /**
18206      * onbeforeLoad - masks the loading area.
18207      *
18208      */
18209     onBeforeLoad : function(store,opts)
18210     {
18211          //Roo.log('onBeforeLoad');   
18212         if (!opts.add) {
18213             this.el.update("");
18214         }
18215         this.el.mask(this.mask ? this.mask : "Loading" ); 
18216     },
18217     onLoad : function ()
18218     {
18219         this.el.unmask();
18220     },
18221     
18222
18223     /**
18224      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18225      * @param {HTMLElement} node
18226      * @return {HTMLElement} The template node
18227      */
18228     findItemFromChild : function(node){
18229         var el = this.dataName  ?
18230             this.el.child('.roo-tpl-' + this.dataName,true) :
18231             this.el.dom; 
18232         
18233         if(!node || node.parentNode == el){
18234                     return node;
18235             }
18236             var p = node.parentNode;
18237             while(p && p != el){
18238             if(p.parentNode == el){
18239                 return p;
18240             }
18241             p = p.parentNode;
18242         }
18243             return null;
18244     },
18245
18246     /** @ignore */
18247     onClick : function(e){
18248         var item = this.findItemFromChild(e.getTarget());
18249         if(item){
18250             var index = this.indexOf(item);
18251             if(this.onItemClick(item, index, e) !== false){
18252                 this.fireEvent("click", this, index, item, e);
18253             }
18254         }else{
18255             this.clearSelections();
18256         }
18257     },
18258
18259     /** @ignore */
18260     onContextMenu : function(e){
18261         var item = this.findItemFromChild(e.getTarget());
18262         if(item){
18263             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18264         }
18265     },
18266
18267     /** @ignore */
18268     onDblClick : function(e){
18269         var item = this.findItemFromChild(e.getTarget());
18270         if(item){
18271             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18272         }
18273     },
18274
18275     onItemClick : function(item, index, e)
18276     {
18277         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18278             return false;
18279         }
18280         if (this.toggleSelect) {
18281             var m = this.isSelected(item) ? 'unselect' : 'select';
18282             //Roo.log(m);
18283             var _t = this;
18284             _t[m](item, true, false);
18285             return true;
18286         }
18287         if(this.multiSelect || this.singleSelect){
18288             if(this.multiSelect && e.shiftKey && this.lastSelection){
18289                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18290             }else{
18291                 this.select(item, this.multiSelect && e.ctrlKey);
18292                 this.lastSelection = item;
18293             }
18294             
18295             if(!this.tickable){
18296                 e.preventDefault();
18297             }
18298             
18299         }
18300         return true;
18301     },
18302
18303     /**
18304      * Get the number of selected nodes.
18305      * @return {Number}
18306      */
18307     getSelectionCount : function(){
18308         return this.selections.length;
18309     },
18310
18311     /**
18312      * Get the currently selected nodes.
18313      * @return {Array} An array of HTMLElements
18314      */
18315     getSelectedNodes : function(){
18316         return this.selections;
18317     },
18318
18319     /**
18320      * Get the indexes of the selected nodes.
18321      * @return {Array}
18322      */
18323     getSelectedIndexes : function(){
18324         var indexes = [], s = this.selections;
18325         for(var i = 0, len = s.length; i < len; i++){
18326             indexes.push(s[i].nodeIndex);
18327         }
18328         return indexes;
18329     },
18330
18331     /**
18332      * Clear all selections
18333      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18334      */
18335     clearSelections : function(suppressEvent){
18336         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18337             this.cmp.elements = this.selections;
18338             this.cmp.removeClass(this.selectedClass);
18339             this.selections = [];
18340             if(!suppressEvent){
18341                 this.fireEvent("selectionchange", this, this.selections);
18342             }
18343         }
18344     },
18345
18346     /**
18347      * Returns true if the passed node is selected
18348      * @param {HTMLElement/Number} node The node or node index
18349      * @return {Boolean}
18350      */
18351     isSelected : function(node){
18352         var s = this.selections;
18353         if(s.length < 1){
18354             return false;
18355         }
18356         node = this.getNode(node);
18357         return s.indexOf(node) !== -1;
18358     },
18359
18360     /**
18361      * Selects nodes.
18362      * @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
18363      * @param {Boolean} keepExisting (optional) true to keep existing selections
18364      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18365      */
18366     select : function(nodeInfo, keepExisting, suppressEvent){
18367         if(nodeInfo instanceof Array){
18368             if(!keepExisting){
18369                 this.clearSelections(true);
18370             }
18371             for(var i = 0, len = nodeInfo.length; i < len; i++){
18372                 this.select(nodeInfo[i], true, true);
18373             }
18374             return;
18375         } 
18376         var node = this.getNode(nodeInfo);
18377         if(!node || this.isSelected(node)){
18378             return; // already selected.
18379         }
18380         if(!keepExisting){
18381             this.clearSelections(true);
18382         }
18383         
18384         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18385             Roo.fly(node).addClass(this.selectedClass);
18386             this.selections.push(node);
18387             if(!suppressEvent){
18388                 this.fireEvent("selectionchange", this, this.selections);
18389             }
18390         }
18391         
18392         
18393     },
18394       /**
18395      * Unselects nodes.
18396      * @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
18397      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18398      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18399      */
18400     unselect : function(nodeInfo, keepExisting, suppressEvent)
18401     {
18402         if(nodeInfo instanceof Array){
18403             Roo.each(this.selections, function(s) {
18404                 this.unselect(s, nodeInfo);
18405             }, this);
18406             return;
18407         }
18408         var node = this.getNode(nodeInfo);
18409         if(!node || !this.isSelected(node)){
18410             //Roo.log("not selected");
18411             return; // not selected.
18412         }
18413         // fireevent???
18414         var ns = [];
18415         Roo.each(this.selections, function(s) {
18416             if (s == node ) {
18417                 Roo.fly(node).removeClass(this.selectedClass);
18418
18419                 return;
18420             }
18421             ns.push(s);
18422         },this);
18423         
18424         this.selections= ns;
18425         this.fireEvent("selectionchange", this, this.selections);
18426     },
18427
18428     /**
18429      * Gets a template node.
18430      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18431      * @return {HTMLElement} The node or null if it wasn't found
18432      */
18433     getNode : function(nodeInfo){
18434         if(typeof nodeInfo == "string"){
18435             return document.getElementById(nodeInfo);
18436         }else if(typeof nodeInfo == "number"){
18437             return this.nodes[nodeInfo];
18438         }
18439         return nodeInfo;
18440     },
18441
18442     /**
18443      * Gets a range template nodes.
18444      * @param {Number} startIndex
18445      * @param {Number} endIndex
18446      * @return {Array} An array of nodes
18447      */
18448     getNodes : function(start, end){
18449         var ns = this.nodes;
18450         start = start || 0;
18451         end = typeof end == "undefined" ? ns.length - 1 : end;
18452         var nodes = [];
18453         if(start <= end){
18454             for(var i = start; i <= end; i++){
18455                 nodes.push(ns[i]);
18456             }
18457         } else{
18458             for(var i = start; i >= end; i--){
18459                 nodes.push(ns[i]);
18460             }
18461         }
18462         return nodes;
18463     },
18464
18465     /**
18466      * Finds the index of the passed node
18467      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18468      * @return {Number} The index of the node or -1
18469      */
18470     indexOf : function(node){
18471         node = this.getNode(node);
18472         if(typeof node.nodeIndex == "number"){
18473             return node.nodeIndex;
18474         }
18475         var ns = this.nodes;
18476         for(var i = 0, len = ns.length; i < len; i++){
18477             if(ns[i] == node){
18478                 return i;
18479             }
18480         }
18481         return -1;
18482     }
18483 });
18484 /*
18485  * - LGPL
18486  *
18487  * based on jquery fullcalendar
18488  * 
18489  */
18490
18491 Roo.bootstrap = Roo.bootstrap || {};
18492 /**
18493  * @class Roo.bootstrap.Calendar
18494  * @extends Roo.bootstrap.Component
18495  * Bootstrap Calendar class
18496  * @cfg {Boolean} loadMask (true|false) default false
18497  * @cfg {Object} header generate the user specific header of the calendar, default false
18498
18499  * @constructor
18500  * Create a new Container
18501  * @param {Object} config The config object
18502  */
18503
18504
18505
18506 Roo.bootstrap.Calendar = function(config){
18507     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18508      this.addEvents({
18509         /**
18510              * @event select
18511              * Fires when a date is selected
18512              * @param {DatePicker} this
18513              * @param {Date} date The selected date
18514              */
18515         'select': true,
18516         /**
18517              * @event monthchange
18518              * Fires when the displayed month changes 
18519              * @param {DatePicker} this
18520              * @param {Date} date The selected month
18521              */
18522         'monthchange': true,
18523         /**
18524              * @event evententer
18525              * Fires when mouse over an event
18526              * @param {Calendar} this
18527              * @param {event} Event
18528              */
18529         'evententer': true,
18530         /**
18531              * @event eventleave
18532              * Fires when the mouse leaves an
18533              * @param {Calendar} this
18534              * @param {event}
18535              */
18536         'eventleave': true,
18537         /**
18538              * @event eventclick
18539              * Fires when the mouse click an
18540              * @param {Calendar} this
18541              * @param {event}
18542              */
18543         'eventclick': true
18544         
18545     });
18546
18547 };
18548
18549 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18550     
18551      /**
18552      * @cfg {Number} startDay
18553      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18554      */
18555     startDay : 0,
18556     
18557     loadMask : false,
18558     
18559     header : false,
18560       
18561     getAutoCreate : function(){
18562         
18563         
18564         var fc_button = function(name, corner, style, content ) {
18565             return Roo.apply({},{
18566                 tag : 'span',
18567                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18568                          (corner.length ?
18569                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18570                             ''
18571                         ),
18572                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18573                 unselectable: 'on'
18574             });
18575         };
18576         
18577         var header = {};
18578         
18579         if(!this.header){
18580             header = {
18581                 tag : 'table',
18582                 cls : 'fc-header',
18583                 style : 'width:100%',
18584                 cn : [
18585                     {
18586                         tag: 'tr',
18587                         cn : [
18588                             {
18589                                 tag : 'td',
18590                                 cls : 'fc-header-left',
18591                                 cn : [
18592                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18593                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18594                                     { tag: 'span', cls: 'fc-header-space' },
18595                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18596
18597
18598                                 ]
18599                             },
18600
18601                             {
18602                                 tag : 'td',
18603                                 cls : 'fc-header-center',
18604                                 cn : [
18605                                     {
18606                                         tag: 'span',
18607                                         cls: 'fc-header-title',
18608                                         cn : {
18609                                             tag: 'H2',
18610                                             html : 'month / year'
18611                                         }
18612                                     }
18613
18614                                 ]
18615                             },
18616                             {
18617                                 tag : 'td',
18618                                 cls : 'fc-header-right',
18619                                 cn : [
18620                               /*      fc_button('month', 'left', '', 'month' ),
18621                                     fc_button('week', '', '', 'week' ),
18622                                     fc_button('day', 'right', '', 'day' )
18623                                 */    
18624
18625                                 ]
18626                             }
18627
18628                         ]
18629                     }
18630                 ]
18631             };
18632         }
18633         
18634         header = this.header;
18635         
18636        
18637         var cal_heads = function() {
18638             var ret = [];
18639             // fixme - handle this.
18640             
18641             for (var i =0; i < Date.dayNames.length; i++) {
18642                 var d = Date.dayNames[i];
18643                 ret.push({
18644                     tag: 'th',
18645                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18646                     html : d.substring(0,3)
18647                 });
18648                 
18649             }
18650             ret[0].cls += ' fc-first';
18651             ret[6].cls += ' fc-last';
18652             return ret;
18653         };
18654         var cal_cell = function(n) {
18655             return  {
18656                 tag: 'td',
18657                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18658                 cn : [
18659                     {
18660                         cn : [
18661                             {
18662                                 cls: 'fc-day-number',
18663                                 html: 'D'
18664                             },
18665                             {
18666                                 cls: 'fc-day-content',
18667                              
18668                                 cn : [
18669                                      {
18670                                         style: 'position: relative;' // height: 17px;
18671                                     }
18672                                 ]
18673                             }
18674                             
18675                             
18676                         ]
18677                     }
18678                 ]
18679                 
18680             }
18681         };
18682         var cal_rows = function() {
18683             
18684             var ret = [];
18685             for (var r = 0; r < 6; r++) {
18686                 var row= {
18687                     tag : 'tr',
18688                     cls : 'fc-week',
18689                     cn : []
18690                 };
18691                 
18692                 for (var i =0; i < Date.dayNames.length; i++) {
18693                     var d = Date.dayNames[i];
18694                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18695
18696                 }
18697                 row.cn[0].cls+=' fc-first';
18698                 row.cn[0].cn[0].style = 'min-height:90px';
18699                 row.cn[6].cls+=' fc-last';
18700                 ret.push(row);
18701                 
18702             }
18703             ret[0].cls += ' fc-first';
18704             ret[4].cls += ' fc-prev-last';
18705             ret[5].cls += ' fc-last';
18706             return ret;
18707             
18708         };
18709         
18710         var cal_table = {
18711             tag: 'table',
18712             cls: 'fc-border-separate',
18713             style : 'width:100%',
18714             cellspacing  : 0,
18715             cn : [
18716                 { 
18717                     tag: 'thead',
18718                     cn : [
18719                         { 
18720                             tag: 'tr',
18721                             cls : 'fc-first fc-last',
18722                             cn : cal_heads()
18723                         }
18724                     ]
18725                 },
18726                 { 
18727                     tag: 'tbody',
18728                     cn : cal_rows()
18729                 }
18730                   
18731             ]
18732         };
18733          
18734          var cfg = {
18735             cls : 'fc fc-ltr',
18736             cn : [
18737                 header,
18738                 {
18739                     cls : 'fc-content',
18740                     style : "position: relative;",
18741                     cn : [
18742                         {
18743                             cls : 'fc-view fc-view-month fc-grid',
18744                             style : 'position: relative',
18745                             unselectable : 'on',
18746                             cn : [
18747                                 {
18748                                     cls : 'fc-event-container',
18749                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18750                                 },
18751                                 cal_table
18752                             ]
18753                         }
18754                     ]
18755     
18756                 }
18757            ] 
18758             
18759         };
18760         
18761          
18762         
18763         return cfg;
18764     },
18765     
18766     
18767     initEvents : function()
18768     {
18769         if(!this.store){
18770             throw "can not find store for calendar";
18771         }
18772         
18773         var mark = {
18774             tag: "div",
18775             cls:"x-dlg-mask",
18776             style: "text-align:center",
18777             cn: [
18778                 {
18779                     tag: "div",
18780                     style: "background-color:white;width:50%;margin:250 auto",
18781                     cn: [
18782                         {
18783                             tag: "img",
18784                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18785                         },
18786                         {
18787                             tag: "span",
18788                             html: "Loading"
18789                         }
18790                         
18791                     ]
18792                 }
18793             ]
18794         };
18795         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18796         
18797         var size = this.el.select('.fc-content', true).first().getSize();
18798         this.maskEl.setSize(size.width, size.height);
18799         this.maskEl.enableDisplayMode("block");
18800         if(!this.loadMask){
18801             this.maskEl.hide();
18802         }
18803         
18804         this.store = Roo.factory(this.store, Roo.data);
18805         this.store.on('load', this.onLoad, this);
18806         this.store.on('beforeload', this.onBeforeLoad, this);
18807         
18808         this.resize();
18809         
18810         this.cells = this.el.select('.fc-day',true);
18811         //Roo.log(this.cells);
18812         this.textNodes = this.el.query('.fc-day-number');
18813         this.cells.addClassOnOver('fc-state-hover');
18814         
18815         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18816         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18817         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18818         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18819         
18820         this.on('monthchange', this.onMonthChange, this);
18821         
18822         this.update(new Date().clearTime());
18823     },
18824     
18825     resize : function() {
18826         var sz  = this.el.getSize();
18827         
18828         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18829         this.el.select('.fc-day-content div',true).setHeight(34);
18830     },
18831     
18832     
18833     // private
18834     showPrevMonth : function(e){
18835         this.update(this.activeDate.add("mo", -1));
18836     },
18837     showToday : function(e){
18838         this.update(new Date().clearTime());
18839     },
18840     // private
18841     showNextMonth : function(e){
18842         this.update(this.activeDate.add("mo", 1));
18843     },
18844
18845     // private
18846     showPrevYear : function(){
18847         this.update(this.activeDate.add("y", -1));
18848     },
18849
18850     // private
18851     showNextYear : function(){
18852         this.update(this.activeDate.add("y", 1));
18853     },
18854
18855     
18856    // private
18857     update : function(date)
18858     {
18859         var vd = this.activeDate;
18860         this.activeDate = date;
18861 //        if(vd && this.el){
18862 //            var t = date.getTime();
18863 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18864 //                Roo.log('using add remove');
18865 //                
18866 //                this.fireEvent('monthchange', this, date);
18867 //                
18868 //                this.cells.removeClass("fc-state-highlight");
18869 //                this.cells.each(function(c){
18870 //                   if(c.dateValue == t){
18871 //                       c.addClass("fc-state-highlight");
18872 //                       setTimeout(function(){
18873 //                            try{c.dom.firstChild.focus();}catch(e){}
18874 //                       }, 50);
18875 //                       return false;
18876 //                   }
18877 //                   return true;
18878 //                });
18879 //                return;
18880 //            }
18881 //        }
18882         
18883         var days = date.getDaysInMonth();
18884         
18885         var firstOfMonth = date.getFirstDateOfMonth();
18886         var startingPos = firstOfMonth.getDay()-this.startDay;
18887         
18888         if(startingPos < this.startDay){
18889             startingPos += 7;
18890         }
18891         
18892         var pm = date.add(Date.MONTH, -1);
18893         var prevStart = pm.getDaysInMonth()-startingPos;
18894 //        
18895         this.cells = this.el.select('.fc-day',true);
18896         this.textNodes = this.el.query('.fc-day-number');
18897         this.cells.addClassOnOver('fc-state-hover');
18898         
18899         var cells = this.cells.elements;
18900         var textEls = this.textNodes;
18901         
18902         Roo.each(cells, function(cell){
18903             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18904         });
18905         
18906         days += startingPos;
18907
18908         // convert everything to numbers so it's fast
18909         var day = 86400000;
18910         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18911         //Roo.log(d);
18912         //Roo.log(pm);
18913         //Roo.log(prevStart);
18914         
18915         var today = new Date().clearTime().getTime();
18916         var sel = date.clearTime().getTime();
18917         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18918         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18919         var ddMatch = this.disabledDatesRE;
18920         var ddText = this.disabledDatesText;
18921         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18922         var ddaysText = this.disabledDaysText;
18923         var format = this.format;
18924         
18925         var setCellClass = function(cal, cell){
18926             cell.row = 0;
18927             cell.events = [];
18928             cell.more = [];
18929             //Roo.log('set Cell Class');
18930             cell.title = "";
18931             var t = d.getTime();
18932             
18933             //Roo.log(d);
18934             
18935             cell.dateValue = t;
18936             if(t == today){
18937                 cell.className += " fc-today";
18938                 cell.className += " fc-state-highlight";
18939                 cell.title = cal.todayText;
18940             }
18941             if(t == sel){
18942                 // disable highlight in other month..
18943                 //cell.className += " fc-state-highlight";
18944                 
18945             }
18946             // disabling
18947             if(t < min) {
18948                 cell.className = " fc-state-disabled";
18949                 cell.title = cal.minText;
18950                 return;
18951             }
18952             if(t > max) {
18953                 cell.className = " fc-state-disabled";
18954                 cell.title = cal.maxText;
18955                 return;
18956             }
18957             if(ddays){
18958                 if(ddays.indexOf(d.getDay()) != -1){
18959                     cell.title = ddaysText;
18960                     cell.className = " fc-state-disabled";
18961                 }
18962             }
18963             if(ddMatch && format){
18964                 var fvalue = d.dateFormat(format);
18965                 if(ddMatch.test(fvalue)){
18966                     cell.title = ddText.replace("%0", fvalue);
18967                     cell.className = " fc-state-disabled";
18968                 }
18969             }
18970             
18971             if (!cell.initialClassName) {
18972                 cell.initialClassName = cell.dom.className;
18973             }
18974             
18975             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
18976         };
18977
18978         var i = 0;
18979         
18980         for(; i < startingPos; i++) {
18981             textEls[i].innerHTML = (++prevStart);
18982             d.setDate(d.getDate()+1);
18983             
18984             cells[i].className = "fc-past fc-other-month";
18985             setCellClass(this, cells[i]);
18986         }
18987         
18988         var intDay = 0;
18989         
18990         for(; i < days; i++){
18991             intDay = i - startingPos + 1;
18992             textEls[i].innerHTML = (intDay);
18993             d.setDate(d.getDate()+1);
18994             
18995             cells[i].className = ''; // "x-date-active";
18996             setCellClass(this, cells[i]);
18997         }
18998         var extraDays = 0;
18999         
19000         for(; i < 42; i++) {
19001             textEls[i].innerHTML = (++extraDays);
19002             d.setDate(d.getDate()+1);
19003             
19004             cells[i].className = "fc-future fc-other-month";
19005             setCellClass(this, cells[i]);
19006         }
19007         
19008         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19009         
19010         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19011         
19012         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19013         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19014         
19015         if(totalRows != 6){
19016             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19017             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19018         }
19019         
19020         this.fireEvent('monthchange', this, date);
19021         
19022         
19023         /*
19024         if(!this.internalRender){
19025             var main = this.el.dom.firstChild;
19026             var w = main.offsetWidth;
19027             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19028             Roo.fly(main).setWidth(w);
19029             this.internalRender = true;
19030             // opera does not respect the auto grow header center column
19031             // then, after it gets a width opera refuses to recalculate
19032             // without a second pass
19033             if(Roo.isOpera && !this.secondPass){
19034                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19035                 this.secondPass = true;
19036                 this.update.defer(10, this, [date]);
19037             }
19038         }
19039         */
19040         
19041     },
19042     
19043     findCell : function(dt) {
19044         dt = dt.clearTime().getTime();
19045         var ret = false;
19046         this.cells.each(function(c){
19047             //Roo.log("check " +c.dateValue + '?=' + dt);
19048             if(c.dateValue == dt){
19049                 ret = c;
19050                 return false;
19051             }
19052             return true;
19053         });
19054         
19055         return ret;
19056     },
19057     
19058     findCells : function(ev) {
19059         var s = ev.start.clone().clearTime().getTime();
19060        // Roo.log(s);
19061         var e= ev.end.clone().clearTime().getTime();
19062        // Roo.log(e);
19063         var ret = [];
19064         this.cells.each(function(c){
19065              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19066             
19067             if(c.dateValue > e){
19068                 return ;
19069             }
19070             if(c.dateValue < s){
19071                 return ;
19072             }
19073             ret.push(c);
19074         });
19075         
19076         return ret;    
19077     },
19078     
19079 //    findBestRow: function(cells)
19080 //    {
19081 //        var ret = 0;
19082 //        
19083 //        for (var i =0 ; i < cells.length;i++) {
19084 //            ret  = Math.max(cells[i].rows || 0,ret);
19085 //        }
19086 //        return ret;
19087 //        
19088 //    },
19089     
19090     
19091     addItem : function(ev)
19092     {
19093         // look for vertical location slot in
19094         var cells = this.findCells(ev);
19095         
19096 //        ev.row = this.findBestRow(cells);
19097         
19098         // work out the location.
19099         
19100         var crow = false;
19101         var rows = [];
19102         for(var i =0; i < cells.length; i++) {
19103             
19104             cells[i].row = cells[0].row;
19105             
19106             if(i == 0){
19107                 cells[i].row = cells[i].row + 1;
19108             }
19109             
19110             if (!crow) {
19111                 crow = {
19112                     start : cells[i],
19113                     end :  cells[i]
19114                 };
19115                 continue;
19116             }
19117             if (crow.start.getY() == cells[i].getY()) {
19118                 // on same row.
19119                 crow.end = cells[i];
19120                 continue;
19121             }
19122             // different row.
19123             rows.push(crow);
19124             crow = {
19125                 start: cells[i],
19126                 end : cells[i]
19127             };
19128             
19129         }
19130         
19131         rows.push(crow);
19132         ev.els = [];
19133         ev.rows = rows;
19134         ev.cells = cells;
19135         
19136         cells[0].events.push(ev);
19137         
19138         this.calevents.push(ev);
19139     },
19140     
19141     clearEvents: function() {
19142         
19143         if(!this.calevents){
19144             return;
19145         }
19146         
19147         Roo.each(this.cells.elements, function(c){
19148             c.row = 0;
19149             c.events = [];
19150             c.more = [];
19151         });
19152         
19153         Roo.each(this.calevents, function(e) {
19154             Roo.each(e.els, function(el) {
19155                 el.un('mouseenter' ,this.onEventEnter, this);
19156                 el.un('mouseleave' ,this.onEventLeave, this);
19157                 el.remove();
19158             },this);
19159         },this);
19160         
19161         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19162             e.remove();
19163         });
19164         
19165     },
19166     
19167     renderEvents: function()
19168     {   
19169         var _this = this;
19170         
19171         this.cells.each(function(c) {
19172             
19173             if(c.row < 5){
19174                 return;
19175             }
19176             
19177             var ev = c.events;
19178             
19179             var r = 4;
19180             if(c.row != c.events.length){
19181                 r = 4 - (4 - (c.row - c.events.length));
19182             }
19183             
19184             c.events = ev.slice(0, r);
19185             c.more = ev.slice(r);
19186             
19187             if(c.more.length && c.more.length == 1){
19188                 c.events.push(c.more.pop());
19189             }
19190             
19191             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19192             
19193         });
19194             
19195         this.cells.each(function(c) {
19196             
19197             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19198             
19199             
19200             for (var e = 0; e < c.events.length; e++){
19201                 var ev = c.events[e];
19202                 var rows = ev.rows;
19203                 
19204                 for(var i = 0; i < rows.length; i++) {
19205                 
19206                     // how many rows should it span..
19207
19208                     var  cfg = {
19209                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19210                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19211
19212                         unselectable : "on",
19213                         cn : [
19214                             {
19215                                 cls: 'fc-event-inner',
19216                                 cn : [
19217     //                                {
19218     //                                  tag:'span',
19219     //                                  cls: 'fc-event-time',
19220     //                                  html : cells.length > 1 ? '' : ev.time
19221     //                                },
19222                                     {
19223                                       tag:'span',
19224                                       cls: 'fc-event-title',
19225                                       html : String.format('{0}', ev.title)
19226                                     }
19227
19228
19229                                 ]
19230                             },
19231                             {
19232                                 cls: 'ui-resizable-handle ui-resizable-e',
19233                                 html : '&nbsp;&nbsp;&nbsp'
19234                             }
19235
19236                         ]
19237                     };
19238
19239                     if (i == 0) {
19240                         cfg.cls += ' fc-event-start';
19241                     }
19242                     if ((i+1) == rows.length) {
19243                         cfg.cls += ' fc-event-end';
19244                     }
19245
19246                     var ctr = _this.el.select('.fc-event-container',true).first();
19247                     var cg = ctr.createChild(cfg);
19248
19249                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19250                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19251
19252                     var r = (c.more.length) ? 1 : 0;
19253                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19254                     cg.setWidth(ebox.right - sbox.x -2);
19255
19256                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19257                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19258                     cg.on('click', _this.onEventClick, _this, ev);
19259
19260                     ev.els.push(cg);
19261                     
19262                 }
19263                 
19264             }
19265             
19266             
19267             if(c.more.length){
19268                 var  cfg = {
19269                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19270                     style : 'position: absolute',
19271                     unselectable : "on",
19272                     cn : [
19273                         {
19274                             cls: 'fc-event-inner',
19275                             cn : [
19276                                 {
19277                                   tag:'span',
19278                                   cls: 'fc-event-title',
19279                                   html : 'More'
19280                                 }
19281
19282
19283                             ]
19284                         },
19285                         {
19286                             cls: 'ui-resizable-handle ui-resizable-e',
19287                             html : '&nbsp;&nbsp;&nbsp'
19288                         }
19289
19290                     ]
19291                 };
19292
19293                 var ctr = _this.el.select('.fc-event-container',true).first();
19294                 var cg = ctr.createChild(cfg);
19295
19296                 var sbox = c.select('.fc-day-content',true).first().getBox();
19297                 var ebox = c.select('.fc-day-content',true).first().getBox();
19298                 //Roo.log(cg);
19299                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19300                 cg.setWidth(ebox.right - sbox.x -2);
19301
19302                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19303                 
19304             }
19305             
19306         });
19307         
19308         
19309         
19310     },
19311     
19312     onEventEnter: function (e, el,event,d) {
19313         this.fireEvent('evententer', this, el, event);
19314     },
19315     
19316     onEventLeave: function (e, el,event,d) {
19317         this.fireEvent('eventleave', this, el, event);
19318     },
19319     
19320     onEventClick: function (e, el,event,d) {
19321         this.fireEvent('eventclick', this, el, event);
19322     },
19323     
19324     onMonthChange: function () {
19325         this.store.load();
19326     },
19327     
19328     onMoreEventClick: function(e, el, more)
19329     {
19330         var _this = this;
19331         
19332         this.calpopover.placement = 'right';
19333         this.calpopover.setTitle('More');
19334         
19335         this.calpopover.setContent('');
19336         
19337         var ctr = this.calpopover.el.select('.popover-content', true).first();
19338         
19339         Roo.each(more, function(m){
19340             var cfg = {
19341                 cls : 'fc-event-hori fc-event-draggable',
19342                 html : m.title
19343             };
19344             var cg = ctr.createChild(cfg);
19345             
19346             cg.on('click', _this.onEventClick, _this, m);
19347         });
19348         
19349         this.calpopover.show(el);
19350         
19351         
19352     },
19353     
19354     onLoad: function () 
19355     {   
19356         this.calevents = [];
19357         var cal = this;
19358         
19359         if(this.store.getCount() > 0){
19360             this.store.data.each(function(d){
19361                cal.addItem({
19362                     id : d.data.id,
19363                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19364                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19365                     time : d.data.start_time,
19366                     title : d.data.title,
19367                     description : d.data.description,
19368                     venue : d.data.venue
19369                 });
19370             });
19371         }
19372         
19373         this.renderEvents();
19374         
19375         if(this.calevents.length && this.loadMask){
19376             this.maskEl.hide();
19377         }
19378     },
19379     
19380     onBeforeLoad: function()
19381     {
19382         this.clearEvents();
19383         if(this.loadMask){
19384             this.maskEl.show();
19385         }
19386     }
19387 });
19388
19389  
19390  /*
19391  * - LGPL
19392  *
19393  * element
19394  * 
19395  */
19396
19397 /**
19398  * @class Roo.bootstrap.Popover
19399  * @extends Roo.bootstrap.Component
19400  * Bootstrap Popover class
19401  * @cfg {String} html contents of the popover   (or false to use children..)
19402  * @cfg {String} title of popover (or false to hide)
19403  * @cfg {String} placement how it is placed
19404  * @cfg {String} trigger click || hover (or false to trigger manually)
19405  * @cfg {String} over what (parent or false to trigger manually.)
19406  * @cfg {Number} delay - delay before showing
19407  
19408  * @constructor
19409  * Create a new Popover
19410  * @param {Object} config The config object
19411  */
19412
19413 Roo.bootstrap.Popover = function(config){
19414     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19415     
19416     this.addEvents({
19417         // raw events
19418          /**
19419          * @event show
19420          * After the popover show
19421          * 
19422          * @param {Roo.bootstrap.Popover} this
19423          */
19424         "show" : true,
19425         /**
19426          * @event hide
19427          * After the popover hide
19428          * 
19429          * @param {Roo.bootstrap.Popover} this
19430          */
19431         "hide" : true
19432     });
19433 };
19434
19435 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19436     
19437     title: 'Fill in a title',
19438     html: false,
19439     
19440     placement : 'right',
19441     trigger : 'hover', // hover
19442     
19443     delay : 0,
19444     
19445     over: 'parent',
19446     
19447     can_build_overlaid : false,
19448     
19449     getChildContainer : function()
19450     {
19451         return this.el.select('.popover-content',true).first();
19452     },
19453     
19454     getAutoCreate : function(){
19455          
19456         var cfg = {
19457            cls : 'popover roo-dynamic',
19458            style: 'display:block',
19459            cn : [
19460                 {
19461                     cls : 'arrow'
19462                 },
19463                 {
19464                     cls : 'popover-inner',
19465                     cn : [
19466                         {
19467                             tag: 'h3',
19468                             cls: 'popover-title popover-header',
19469                             html : this.title
19470                         },
19471                         {
19472                             cls : 'popover-content popover-body',
19473                             html : this.html
19474                         }
19475                     ]
19476                     
19477                 }
19478            ]
19479         };
19480         
19481         return cfg;
19482     },
19483     setTitle: function(str)
19484     {
19485         this.title = str;
19486         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19487     },
19488     setContent: function(str)
19489     {
19490         this.html = str;
19491         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19492     },
19493     // as it get's added to the bottom of the page.
19494     onRender : function(ct, position)
19495     {
19496         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19497         if(!this.el){
19498             var cfg = Roo.apply({},  this.getAutoCreate());
19499             cfg.id = Roo.id();
19500             
19501             if (this.cls) {
19502                 cfg.cls += ' ' + this.cls;
19503             }
19504             if (this.style) {
19505                 cfg.style = this.style;
19506             }
19507             //Roo.log("adding to ");
19508             this.el = Roo.get(document.body).createChild(cfg, position);
19509 //            Roo.log(this.el);
19510         }
19511         this.initEvents();
19512     },
19513     
19514     initEvents : function()
19515     {
19516         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19517         this.el.enableDisplayMode('block');
19518         this.el.hide();
19519         if (this.over === false) {
19520             return; 
19521         }
19522         if (this.triggers === false) {
19523             return;
19524         }
19525         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19526         var triggers = this.trigger ? this.trigger.split(' ') : [];
19527         Roo.each(triggers, function(trigger) {
19528         
19529             if (trigger == 'click') {
19530                 on_el.on('click', this.toggle, this);
19531             } else if (trigger != 'manual') {
19532                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19533                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19534       
19535                 on_el.on(eventIn  ,this.enter, this);
19536                 on_el.on(eventOut, this.leave, this);
19537             }
19538         }, this);
19539         
19540     },
19541     
19542     
19543     // private
19544     timeout : null,
19545     hoverState : null,
19546     
19547     toggle : function () {
19548         this.hoverState == 'in' ? this.leave() : this.enter();
19549     },
19550     
19551     enter : function () {
19552         
19553         clearTimeout(this.timeout);
19554     
19555         this.hoverState = 'in';
19556     
19557         if (!this.delay || !this.delay.show) {
19558             this.show();
19559             return;
19560         }
19561         var _t = this;
19562         this.timeout = setTimeout(function () {
19563             if (_t.hoverState == 'in') {
19564                 _t.show();
19565             }
19566         }, this.delay.show)
19567     },
19568     
19569     leave : function() {
19570         clearTimeout(this.timeout);
19571     
19572         this.hoverState = 'out';
19573     
19574         if (!this.delay || !this.delay.hide) {
19575             this.hide();
19576             return;
19577         }
19578         var _t = this;
19579         this.timeout = setTimeout(function () {
19580             if (_t.hoverState == 'out') {
19581                 _t.hide();
19582             }
19583         }, this.delay.hide)
19584     },
19585     
19586     show : function (on_el)
19587     {
19588         if (!on_el) {
19589             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19590         }
19591         
19592         // set content.
19593         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19594         if (this.html !== false) {
19595             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19596         }
19597         this.el.removeClass([
19598             'fade','top','bottom', 'left', 'right','in',
19599             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19600         ]);
19601         if (!this.title.length) {
19602             this.el.select('.popover-title',true).hide();
19603         }
19604         
19605         var placement = typeof this.placement == 'function' ?
19606             this.placement.call(this, this.el, on_el) :
19607             this.placement;
19608             
19609         var autoToken = /\s?auto?\s?/i;
19610         var autoPlace = autoToken.test(placement);
19611         if (autoPlace) {
19612             placement = placement.replace(autoToken, '') || 'top';
19613         }
19614         
19615         //this.el.detach()
19616         //this.el.setXY([0,0]);
19617         this.el.show();
19618         this.el.dom.style.display='block';
19619         this.el.addClass(placement);
19620         
19621         //this.el.appendTo(on_el);
19622         
19623         var p = this.getPosition();
19624         var box = this.el.getBox();
19625         
19626         if (autoPlace) {
19627             // fixme..
19628         }
19629         var align = Roo.bootstrap.Popover.alignment[placement];
19630         
19631 //        Roo.log(align);
19632         this.el.alignTo(on_el, align[0],align[1]);
19633         //var arrow = this.el.select('.arrow',true).first();
19634         //arrow.set(align[2], 
19635         
19636         this.el.addClass('in');
19637         
19638         
19639         if (this.el.hasClass('fade')) {
19640             // fade it?
19641         }
19642         
19643         this.hoverState = 'in';
19644         
19645         this.fireEvent('show', this);
19646         
19647     },
19648     hide : function()
19649     {
19650         this.el.setXY([0,0]);
19651         this.el.removeClass('in');
19652         this.el.hide();
19653         this.hoverState = null;
19654         
19655         this.fireEvent('hide', this);
19656     }
19657     
19658 });
19659
19660 Roo.bootstrap.Popover.alignment = {
19661     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19662     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19663     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19664     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19665 };
19666
19667  /*
19668  * - LGPL
19669  *
19670  * Progress
19671  * 
19672  */
19673
19674 /**
19675  * @class Roo.bootstrap.Progress
19676  * @extends Roo.bootstrap.Component
19677  * Bootstrap Progress class
19678  * @cfg {Boolean} striped striped of the progress bar
19679  * @cfg {Boolean} active animated of the progress bar
19680  * 
19681  * 
19682  * @constructor
19683  * Create a new Progress
19684  * @param {Object} config The config object
19685  */
19686
19687 Roo.bootstrap.Progress = function(config){
19688     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19689 };
19690
19691 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19692     
19693     striped : false,
19694     active: false,
19695     
19696     getAutoCreate : function(){
19697         var cfg = {
19698             tag: 'div',
19699             cls: 'progress'
19700         };
19701         
19702         
19703         if(this.striped){
19704             cfg.cls += ' progress-striped';
19705         }
19706       
19707         if(this.active){
19708             cfg.cls += ' active';
19709         }
19710         
19711         
19712         return cfg;
19713     }
19714    
19715 });
19716
19717  
19718
19719  /*
19720  * - LGPL
19721  *
19722  * ProgressBar
19723  * 
19724  */
19725
19726 /**
19727  * @class Roo.bootstrap.ProgressBar
19728  * @extends Roo.bootstrap.Component
19729  * Bootstrap ProgressBar class
19730  * @cfg {Number} aria_valuenow aria-value now
19731  * @cfg {Number} aria_valuemin aria-value min
19732  * @cfg {Number} aria_valuemax aria-value max
19733  * @cfg {String} label label for the progress bar
19734  * @cfg {String} panel (success | info | warning | danger )
19735  * @cfg {String} role role of the progress bar
19736  * @cfg {String} sr_only text
19737  * 
19738  * 
19739  * @constructor
19740  * Create a new ProgressBar
19741  * @param {Object} config The config object
19742  */
19743
19744 Roo.bootstrap.ProgressBar = function(config){
19745     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19746 };
19747
19748 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19749     
19750     aria_valuenow : 0,
19751     aria_valuemin : 0,
19752     aria_valuemax : 100,
19753     label : false,
19754     panel : false,
19755     role : false,
19756     sr_only: false,
19757     
19758     getAutoCreate : function()
19759     {
19760         
19761         var cfg = {
19762             tag: 'div',
19763             cls: 'progress-bar',
19764             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19765         };
19766         
19767         if(this.sr_only){
19768             cfg.cn = {
19769                 tag: 'span',
19770                 cls: 'sr-only',
19771                 html: this.sr_only
19772             }
19773         }
19774         
19775         if(this.role){
19776             cfg.role = this.role;
19777         }
19778         
19779         if(this.aria_valuenow){
19780             cfg['aria-valuenow'] = this.aria_valuenow;
19781         }
19782         
19783         if(this.aria_valuemin){
19784             cfg['aria-valuemin'] = this.aria_valuemin;
19785         }
19786         
19787         if(this.aria_valuemax){
19788             cfg['aria-valuemax'] = this.aria_valuemax;
19789         }
19790         
19791         if(this.label && !this.sr_only){
19792             cfg.html = this.label;
19793         }
19794         
19795         if(this.panel){
19796             cfg.cls += ' progress-bar-' + this.panel;
19797         }
19798         
19799         return cfg;
19800     },
19801     
19802     update : function(aria_valuenow)
19803     {
19804         this.aria_valuenow = aria_valuenow;
19805         
19806         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19807     }
19808    
19809 });
19810
19811  
19812
19813  /*
19814  * - LGPL
19815  *
19816  * column
19817  * 
19818  */
19819
19820 /**
19821  * @class Roo.bootstrap.TabGroup
19822  * @extends Roo.bootstrap.Column
19823  * Bootstrap Column class
19824  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19825  * @cfg {Boolean} carousel true to make the group behave like a carousel
19826  * @cfg {Boolean} bullets show bullets for the panels
19827  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19828  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19829  * @cfg {Boolean} showarrow (true|false) show arrow default true
19830  * 
19831  * @constructor
19832  * Create a new TabGroup
19833  * @param {Object} config The config object
19834  */
19835
19836 Roo.bootstrap.TabGroup = function(config){
19837     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19838     if (!this.navId) {
19839         this.navId = Roo.id();
19840     }
19841     this.tabs = [];
19842     Roo.bootstrap.TabGroup.register(this);
19843     
19844 };
19845
19846 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19847     
19848     carousel : false,
19849     transition : false,
19850     bullets : 0,
19851     timer : 0,
19852     autoslide : false,
19853     slideFn : false,
19854     slideOnTouch : false,
19855     showarrow : true,
19856     
19857     getAutoCreate : function()
19858     {
19859         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19860         
19861         cfg.cls += ' tab-content';
19862         
19863         if (this.carousel) {
19864             cfg.cls += ' carousel slide';
19865             
19866             cfg.cn = [{
19867                cls : 'carousel-inner',
19868                cn : []
19869             }];
19870         
19871             if(this.bullets  && !Roo.isTouch){
19872                 
19873                 var bullets = {
19874                     cls : 'carousel-bullets',
19875                     cn : []
19876                 };
19877                
19878                 if(this.bullets_cls){
19879                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19880                 }
19881                 
19882                 bullets.cn.push({
19883                     cls : 'clear'
19884                 });
19885                 
19886                 cfg.cn[0].cn.push(bullets);
19887             }
19888             
19889             if(this.showarrow){
19890                 cfg.cn[0].cn.push({
19891                     tag : 'div',
19892                     class : 'carousel-arrow',
19893                     cn : [
19894                         {
19895                             tag : 'div',
19896                             class : 'carousel-prev',
19897                             cn : [
19898                                 {
19899                                     tag : 'i',
19900                                     class : 'fa fa-chevron-left'
19901                                 }
19902                             ]
19903                         },
19904                         {
19905                             tag : 'div',
19906                             class : 'carousel-next',
19907                             cn : [
19908                                 {
19909                                     tag : 'i',
19910                                     class : 'fa fa-chevron-right'
19911                                 }
19912                             ]
19913                         }
19914                     ]
19915                 });
19916             }
19917             
19918         }
19919         
19920         return cfg;
19921     },
19922     
19923     initEvents:  function()
19924     {
19925 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19926 //            this.el.on("touchstart", this.onTouchStart, this);
19927 //        }
19928         
19929         if(this.autoslide){
19930             var _this = this;
19931             
19932             this.slideFn = window.setInterval(function() {
19933                 _this.showPanelNext();
19934             }, this.timer);
19935         }
19936         
19937         if(this.showarrow){
19938             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19939             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19940         }
19941         
19942         
19943     },
19944     
19945 //    onTouchStart : function(e, el, o)
19946 //    {
19947 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
19948 //            return;
19949 //        }
19950 //        
19951 //        this.showPanelNext();
19952 //    },
19953     
19954     
19955     getChildContainer : function()
19956     {
19957         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
19958     },
19959     
19960     /**
19961     * register a Navigation item
19962     * @param {Roo.bootstrap.NavItem} the navitem to add
19963     */
19964     register : function(item)
19965     {
19966         this.tabs.push( item);
19967         item.navId = this.navId; // not really needed..
19968         this.addBullet();
19969     
19970     },
19971     
19972     getActivePanel : function()
19973     {
19974         var r = false;
19975         Roo.each(this.tabs, function(t) {
19976             if (t.active) {
19977                 r = t;
19978                 return false;
19979             }
19980             return null;
19981         });
19982         return r;
19983         
19984     },
19985     getPanelByName : function(n)
19986     {
19987         var r = false;
19988         Roo.each(this.tabs, function(t) {
19989             if (t.tabId == n) {
19990                 r = t;
19991                 return false;
19992             }
19993             return null;
19994         });
19995         return r;
19996     },
19997     indexOfPanel : function(p)
19998     {
19999         var r = false;
20000         Roo.each(this.tabs, function(t,i) {
20001             if (t.tabId == p.tabId) {
20002                 r = i;
20003                 return false;
20004             }
20005             return null;
20006         });
20007         return r;
20008     },
20009     /**
20010      * show a specific panel
20011      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20012      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20013      */
20014     showPanel : function (pan)
20015     {
20016         if(this.transition || typeof(pan) == 'undefined'){
20017             Roo.log("waiting for the transitionend");
20018             return false;
20019         }
20020         
20021         if (typeof(pan) == 'number') {
20022             pan = this.tabs[pan];
20023         }
20024         
20025         if (typeof(pan) == 'string') {
20026             pan = this.getPanelByName(pan);
20027         }
20028         
20029         var cur = this.getActivePanel();
20030         
20031         if(!pan || !cur){
20032             Roo.log('pan or acitve pan is undefined');
20033             return false;
20034         }
20035         
20036         if (pan.tabId == this.getActivePanel().tabId) {
20037             return true;
20038         }
20039         
20040         if (false === cur.fireEvent('beforedeactivate')) {
20041             return false;
20042         }
20043         
20044         if(this.bullets > 0 && !Roo.isTouch){
20045             this.setActiveBullet(this.indexOfPanel(pan));
20046         }
20047         
20048         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20049             
20050             //class="carousel-item carousel-item-next carousel-item-left"
20051             
20052             this.transition = true;
20053             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20054             var lr = dir == 'next' ? 'left' : 'right';
20055             pan.el.addClass(dir); // or prev
20056             pan.el.addClass('carousel-item-' + dir); // or prev
20057             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20058             cur.el.addClass(lr); // or right
20059             pan.el.addClass(lr);
20060             cur.el.addClass('carousel-item-' +lr); // or right
20061             pan.el.addClass('carousel-item-' +lr);
20062             
20063             
20064             var _this = this;
20065             cur.el.on('transitionend', function() {
20066                 Roo.log("trans end?");
20067                 
20068                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20069                 pan.setActive(true);
20070                 
20071                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20072                 cur.setActive(false);
20073                 
20074                 _this.transition = false;
20075                 
20076             }, this, { single:  true } );
20077             
20078             return true;
20079         }
20080         
20081         cur.setActive(false);
20082         pan.setActive(true);
20083         
20084         return true;
20085         
20086     },
20087     showPanelNext : function()
20088     {
20089         var i = this.indexOfPanel(this.getActivePanel());
20090         
20091         if (i >= this.tabs.length - 1 && !this.autoslide) {
20092             return;
20093         }
20094         
20095         if (i >= this.tabs.length - 1 && this.autoslide) {
20096             i = -1;
20097         }
20098         
20099         this.showPanel(this.tabs[i+1]);
20100     },
20101     
20102     showPanelPrev : function()
20103     {
20104         var i = this.indexOfPanel(this.getActivePanel());
20105         
20106         if (i  < 1 && !this.autoslide) {
20107             return;
20108         }
20109         
20110         if (i < 1 && this.autoslide) {
20111             i = this.tabs.length;
20112         }
20113         
20114         this.showPanel(this.tabs[i-1]);
20115     },
20116     
20117     
20118     addBullet: function()
20119     {
20120         if(!this.bullets || Roo.isTouch){
20121             return;
20122         }
20123         var ctr = this.el.select('.carousel-bullets',true).first();
20124         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20125         var bullet = ctr.createChild({
20126             cls : 'bullet bullet-' + i
20127         },ctr.dom.lastChild);
20128         
20129         
20130         var _this = this;
20131         
20132         bullet.on('click', (function(e, el, o, ii, t){
20133
20134             e.preventDefault();
20135
20136             this.showPanel(ii);
20137
20138             if(this.autoslide && this.slideFn){
20139                 clearInterval(this.slideFn);
20140                 this.slideFn = window.setInterval(function() {
20141                     _this.showPanelNext();
20142                 }, this.timer);
20143             }
20144
20145         }).createDelegate(this, [i, bullet], true));
20146                 
20147         
20148     },
20149      
20150     setActiveBullet : function(i)
20151     {
20152         if(Roo.isTouch){
20153             return;
20154         }
20155         
20156         Roo.each(this.el.select('.bullet', true).elements, function(el){
20157             el.removeClass('selected');
20158         });
20159
20160         var bullet = this.el.select('.bullet-' + i, true).first();
20161         
20162         if(!bullet){
20163             return;
20164         }
20165         
20166         bullet.addClass('selected');
20167     }
20168     
20169     
20170   
20171 });
20172
20173  
20174
20175  
20176  
20177 Roo.apply(Roo.bootstrap.TabGroup, {
20178     
20179     groups: {},
20180      /**
20181     * register a Navigation Group
20182     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20183     */
20184     register : function(navgrp)
20185     {
20186         this.groups[navgrp.navId] = navgrp;
20187         
20188     },
20189     /**
20190     * fetch a Navigation Group based on the navigation ID
20191     * if one does not exist , it will get created.
20192     * @param {string} the navgroup to add
20193     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20194     */
20195     get: function(navId) {
20196         if (typeof(this.groups[navId]) == 'undefined') {
20197             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20198         }
20199         return this.groups[navId] ;
20200     }
20201     
20202     
20203     
20204 });
20205
20206  /*
20207  * - LGPL
20208  *
20209  * TabPanel
20210  * 
20211  */
20212
20213 /**
20214  * @class Roo.bootstrap.TabPanel
20215  * @extends Roo.bootstrap.Component
20216  * Bootstrap TabPanel class
20217  * @cfg {Boolean} active panel active
20218  * @cfg {String} html panel content
20219  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20220  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20221  * @cfg {String} href click to link..
20222  * 
20223  * 
20224  * @constructor
20225  * Create a new TabPanel
20226  * @param {Object} config The config object
20227  */
20228
20229 Roo.bootstrap.TabPanel = function(config){
20230     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20231     this.addEvents({
20232         /**
20233              * @event changed
20234              * Fires when the active status changes
20235              * @param {Roo.bootstrap.TabPanel} this
20236              * @param {Boolean} state the new state
20237             
20238          */
20239         'changed': true,
20240         /**
20241              * @event beforedeactivate
20242              * Fires before a tab is de-activated - can be used to do validation on a form.
20243              * @param {Roo.bootstrap.TabPanel} this
20244              * @return {Boolean} false if there is an error
20245             
20246          */
20247         'beforedeactivate': true
20248      });
20249     
20250     this.tabId = this.tabId || Roo.id();
20251   
20252 };
20253
20254 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20255     
20256     active: false,
20257     html: false,
20258     tabId: false,
20259     navId : false,
20260     href : '',
20261     
20262     getAutoCreate : function(){
20263         
20264         
20265         var cfg = {
20266             tag: 'div',
20267             // item is needed for carousel - not sure if it has any effect otherwise
20268             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20269             html: this.html || ''
20270         };
20271         
20272         if(this.active){
20273             cfg.cls += ' active';
20274         }
20275         
20276         if(this.tabId){
20277             cfg.tabId = this.tabId;
20278         }
20279         
20280         
20281         
20282         return cfg;
20283     },
20284     
20285     initEvents:  function()
20286     {
20287         var p = this.parent();
20288         
20289         this.navId = this.navId || p.navId;
20290         
20291         if (typeof(this.navId) != 'undefined') {
20292             // not really needed.. but just in case.. parent should be a NavGroup.
20293             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20294             
20295             tg.register(this);
20296             
20297             var i = tg.tabs.length - 1;
20298             
20299             if(this.active && tg.bullets > 0 && i < tg.bullets){
20300                 tg.setActiveBullet(i);
20301             }
20302         }
20303         
20304         this.el.on('click', this.onClick, this);
20305         
20306         if(Roo.isTouch){
20307             this.el.on("touchstart", this.onTouchStart, this);
20308             this.el.on("touchmove", this.onTouchMove, this);
20309             this.el.on("touchend", this.onTouchEnd, this);
20310         }
20311         
20312     },
20313     
20314     onRender : function(ct, position)
20315     {
20316         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20317     },
20318     
20319     setActive : function(state)
20320     {
20321         Roo.log("panel - set active " + this.tabId + "=" + state);
20322         
20323         this.active = state;
20324         if (!state) {
20325             this.el.removeClass('active');
20326             
20327         } else  if (!this.el.hasClass('active')) {
20328             this.el.addClass('active');
20329         }
20330         
20331         this.fireEvent('changed', this, state);
20332     },
20333     
20334     onClick : function(e)
20335     {
20336         e.preventDefault();
20337         
20338         if(!this.href.length){
20339             return;
20340         }
20341         
20342         window.location.href = this.href;
20343     },
20344     
20345     startX : 0,
20346     startY : 0,
20347     endX : 0,
20348     endY : 0,
20349     swiping : false,
20350     
20351     onTouchStart : function(e)
20352     {
20353         this.swiping = false;
20354         
20355         this.startX = e.browserEvent.touches[0].clientX;
20356         this.startY = e.browserEvent.touches[0].clientY;
20357     },
20358     
20359     onTouchMove : function(e)
20360     {
20361         this.swiping = true;
20362         
20363         this.endX = e.browserEvent.touches[0].clientX;
20364         this.endY = e.browserEvent.touches[0].clientY;
20365     },
20366     
20367     onTouchEnd : function(e)
20368     {
20369         if(!this.swiping){
20370             this.onClick(e);
20371             return;
20372         }
20373         
20374         var tabGroup = this.parent();
20375         
20376         if(this.endX > this.startX){ // swiping right
20377             tabGroup.showPanelPrev();
20378             return;
20379         }
20380         
20381         if(this.startX > this.endX){ // swiping left
20382             tabGroup.showPanelNext();
20383             return;
20384         }
20385     }
20386     
20387     
20388 });
20389  
20390
20391  
20392
20393  /*
20394  * - LGPL
20395  *
20396  * DateField
20397  * 
20398  */
20399
20400 /**
20401  * @class Roo.bootstrap.DateField
20402  * @extends Roo.bootstrap.Input
20403  * Bootstrap DateField class
20404  * @cfg {Number} weekStart default 0
20405  * @cfg {String} viewMode default empty, (months|years)
20406  * @cfg {String} minViewMode default empty, (months|years)
20407  * @cfg {Number} startDate default -Infinity
20408  * @cfg {Number} endDate default Infinity
20409  * @cfg {Boolean} todayHighlight default false
20410  * @cfg {Boolean} todayBtn default false
20411  * @cfg {Boolean} calendarWeeks default false
20412  * @cfg {Object} daysOfWeekDisabled default empty
20413  * @cfg {Boolean} singleMode default false (true | false)
20414  * 
20415  * @cfg {Boolean} keyboardNavigation default true
20416  * @cfg {String} language default en
20417  * 
20418  * @constructor
20419  * Create a new DateField
20420  * @param {Object} config The config object
20421  */
20422
20423 Roo.bootstrap.DateField = function(config){
20424     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20425      this.addEvents({
20426             /**
20427              * @event show
20428              * Fires when this field show.
20429              * @param {Roo.bootstrap.DateField} this
20430              * @param {Mixed} date The date value
20431              */
20432             show : true,
20433             /**
20434              * @event show
20435              * Fires when this field hide.
20436              * @param {Roo.bootstrap.DateField} this
20437              * @param {Mixed} date The date value
20438              */
20439             hide : true,
20440             /**
20441              * @event select
20442              * Fires when select a date.
20443              * @param {Roo.bootstrap.DateField} this
20444              * @param {Mixed} date The date value
20445              */
20446             select : true,
20447             /**
20448              * @event beforeselect
20449              * Fires when before select a date.
20450              * @param {Roo.bootstrap.DateField} this
20451              * @param {Mixed} date The date value
20452              */
20453             beforeselect : true
20454         });
20455 };
20456
20457 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20458     
20459     /**
20460      * @cfg {String} format
20461      * The default date format string which can be overriden for localization support.  The format must be
20462      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20463      */
20464     format : "m/d/y",
20465     /**
20466      * @cfg {String} altFormats
20467      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20468      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20469      */
20470     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20471     
20472     weekStart : 0,
20473     
20474     viewMode : '',
20475     
20476     minViewMode : '',
20477     
20478     todayHighlight : false,
20479     
20480     todayBtn: false,
20481     
20482     language: 'en',
20483     
20484     keyboardNavigation: true,
20485     
20486     calendarWeeks: false,
20487     
20488     startDate: -Infinity,
20489     
20490     endDate: Infinity,
20491     
20492     daysOfWeekDisabled: [],
20493     
20494     _events: [],
20495     
20496     singleMode : false,
20497     
20498     UTCDate: function()
20499     {
20500         return new Date(Date.UTC.apply(Date, arguments));
20501     },
20502     
20503     UTCToday: function()
20504     {
20505         var today = new Date();
20506         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20507     },
20508     
20509     getDate: function() {
20510             var d = this.getUTCDate();
20511             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20512     },
20513     
20514     getUTCDate: function() {
20515             return this.date;
20516     },
20517     
20518     setDate: function(d) {
20519             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20520     },
20521     
20522     setUTCDate: function(d) {
20523             this.date = d;
20524             this.setValue(this.formatDate(this.date));
20525     },
20526         
20527     onRender: function(ct, position)
20528     {
20529         
20530         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20531         
20532         this.language = this.language || 'en';
20533         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20534         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20535         
20536         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20537         this.format = this.format || 'm/d/y';
20538         this.isInline = false;
20539         this.isInput = true;
20540         this.component = this.el.select('.add-on', true).first() || false;
20541         this.component = (this.component && this.component.length === 0) ? false : this.component;
20542         this.hasInput = this.component && this.inputEl().length;
20543         
20544         if (typeof(this.minViewMode === 'string')) {
20545             switch (this.minViewMode) {
20546                 case 'months':
20547                     this.minViewMode = 1;
20548                     break;
20549                 case 'years':
20550                     this.minViewMode = 2;
20551                     break;
20552                 default:
20553                     this.minViewMode = 0;
20554                     break;
20555             }
20556         }
20557         
20558         if (typeof(this.viewMode === 'string')) {
20559             switch (this.viewMode) {
20560                 case 'months':
20561                     this.viewMode = 1;
20562                     break;
20563                 case 'years':
20564                     this.viewMode = 2;
20565                     break;
20566                 default:
20567                     this.viewMode = 0;
20568                     break;
20569             }
20570         }
20571                 
20572         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20573         
20574 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20575         
20576         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20577         
20578         this.picker().on('mousedown', this.onMousedown, this);
20579         this.picker().on('click', this.onClick, this);
20580         
20581         this.picker().addClass('datepicker-dropdown');
20582         
20583         this.startViewMode = this.viewMode;
20584         
20585         if(this.singleMode){
20586             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20587                 v.setVisibilityMode(Roo.Element.DISPLAY);
20588                 v.hide();
20589             });
20590             
20591             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20592                 v.setStyle('width', '189px');
20593             });
20594         }
20595         
20596         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20597             if(!this.calendarWeeks){
20598                 v.remove();
20599                 return;
20600             }
20601             
20602             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20603             v.attr('colspan', function(i, val){
20604                 return parseInt(val) + 1;
20605             });
20606         });
20607                         
20608         
20609         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20610         
20611         this.setStartDate(this.startDate);
20612         this.setEndDate(this.endDate);
20613         
20614         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20615         
20616         this.fillDow();
20617         this.fillMonths();
20618         this.update();
20619         this.showMode();
20620         
20621         if(this.isInline) {
20622             this.showPopup();
20623         }
20624     },
20625     
20626     picker : function()
20627     {
20628         return this.pickerEl;
20629 //        return this.el.select('.datepicker', true).first();
20630     },
20631     
20632     fillDow: function()
20633     {
20634         var dowCnt = this.weekStart;
20635         
20636         var dow = {
20637             tag: 'tr',
20638             cn: [
20639                 
20640             ]
20641         };
20642         
20643         if(this.calendarWeeks){
20644             dow.cn.push({
20645                 tag: 'th',
20646                 cls: 'cw',
20647                 html: '&nbsp;'
20648             })
20649         }
20650         
20651         while (dowCnt < this.weekStart + 7) {
20652             dow.cn.push({
20653                 tag: 'th',
20654                 cls: 'dow',
20655                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20656             });
20657         }
20658         
20659         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20660     },
20661     
20662     fillMonths: function()
20663     {    
20664         var i = 0;
20665         var months = this.picker().select('>.datepicker-months td', true).first();
20666         
20667         months.dom.innerHTML = '';
20668         
20669         while (i < 12) {
20670             var month = {
20671                 tag: 'span',
20672                 cls: 'month',
20673                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20674             };
20675             
20676             months.createChild(month);
20677         }
20678         
20679     },
20680     
20681     update: function()
20682     {
20683         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;
20684         
20685         if (this.date < this.startDate) {
20686             this.viewDate = new Date(this.startDate);
20687         } else if (this.date > this.endDate) {
20688             this.viewDate = new Date(this.endDate);
20689         } else {
20690             this.viewDate = new Date(this.date);
20691         }
20692         
20693         this.fill();
20694     },
20695     
20696     fill: function() 
20697     {
20698         var d = new Date(this.viewDate),
20699                 year = d.getUTCFullYear(),
20700                 month = d.getUTCMonth(),
20701                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20702                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20703                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20704                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20705                 currentDate = this.date && this.date.valueOf(),
20706                 today = this.UTCToday();
20707         
20708         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20709         
20710 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20711         
20712 //        this.picker.select('>tfoot th.today').
20713 //                                              .text(dates[this.language].today)
20714 //                                              .toggle(this.todayBtn !== false);
20715     
20716         this.updateNavArrows();
20717         this.fillMonths();
20718                                                 
20719         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20720         
20721         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20722          
20723         prevMonth.setUTCDate(day);
20724         
20725         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20726         
20727         var nextMonth = new Date(prevMonth);
20728         
20729         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20730         
20731         nextMonth = nextMonth.valueOf();
20732         
20733         var fillMonths = false;
20734         
20735         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20736         
20737         while(prevMonth.valueOf() <= nextMonth) {
20738             var clsName = '';
20739             
20740             if (prevMonth.getUTCDay() === this.weekStart) {
20741                 if(fillMonths){
20742                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20743                 }
20744                     
20745                 fillMonths = {
20746                     tag: 'tr',
20747                     cn: []
20748                 };
20749                 
20750                 if(this.calendarWeeks){
20751                     // ISO 8601: First week contains first thursday.
20752                     // ISO also states week starts on Monday, but we can be more abstract here.
20753                     var
20754                     // Start of current week: based on weekstart/current date
20755                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20756                     // Thursday of this week
20757                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20758                     // First Thursday of year, year from thursday
20759                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20760                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20761                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20762                     
20763                     fillMonths.cn.push({
20764                         tag: 'td',
20765                         cls: 'cw',
20766                         html: calWeek
20767                     });
20768                 }
20769             }
20770             
20771             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20772                 clsName += ' old';
20773             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20774                 clsName += ' new';
20775             }
20776             if (this.todayHighlight &&
20777                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20778                 prevMonth.getUTCMonth() == today.getMonth() &&
20779                 prevMonth.getUTCDate() == today.getDate()) {
20780                 clsName += ' today';
20781             }
20782             
20783             if (currentDate && prevMonth.valueOf() === currentDate) {
20784                 clsName += ' active';
20785             }
20786             
20787             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20788                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20789                     clsName += ' disabled';
20790             }
20791             
20792             fillMonths.cn.push({
20793                 tag: 'td',
20794                 cls: 'day ' + clsName,
20795                 html: prevMonth.getDate()
20796             });
20797             
20798             prevMonth.setDate(prevMonth.getDate()+1);
20799         }
20800           
20801         var currentYear = this.date && this.date.getUTCFullYear();
20802         var currentMonth = this.date && this.date.getUTCMonth();
20803         
20804         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20805         
20806         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20807             v.removeClass('active');
20808             
20809             if(currentYear === year && k === currentMonth){
20810                 v.addClass('active');
20811             }
20812             
20813             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20814                 v.addClass('disabled');
20815             }
20816             
20817         });
20818         
20819         
20820         year = parseInt(year/10, 10) * 10;
20821         
20822         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20823         
20824         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20825         
20826         year -= 1;
20827         for (var i = -1; i < 11; i++) {
20828             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20829                 tag: 'span',
20830                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20831                 html: year
20832             });
20833             
20834             year += 1;
20835         }
20836     },
20837     
20838     showMode: function(dir) 
20839     {
20840         if (dir) {
20841             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20842         }
20843         
20844         Roo.each(this.picker().select('>div',true).elements, function(v){
20845             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20846             v.hide();
20847         });
20848         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20849     },
20850     
20851     place: function()
20852     {
20853         if(this.isInline) {
20854             return;
20855         }
20856         
20857         this.picker().removeClass(['bottom', 'top']);
20858         
20859         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20860             /*
20861              * place to the top of element!
20862              *
20863              */
20864             
20865             this.picker().addClass('top');
20866             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20867             
20868             return;
20869         }
20870         
20871         this.picker().addClass('bottom');
20872         
20873         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20874     },
20875     
20876     parseDate : function(value)
20877     {
20878         if(!value || value instanceof Date){
20879             return value;
20880         }
20881         var v = Date.parseDate(value, this.format);
20882         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20883             v = Date.parseDate(value, 'Y-m-d');
20884         }
20885         if(!v && this.altFormats){
20886             if(!this.altFormatsArray){
20887                 this.altFormatsArray = this.altFormats.split("|");
20888             }
20889             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20890                 v = Date.parseDate(value, this.altFormatsArray[i]);
20891             }
20892         }
20893         return v;
20894     },
20895     
20896     formatDate : function(date, fmt)
20897     {   
20898         return (!date || !(date instanceof Date)) ?
20899         date : date.dateFormat(fmt || this.format);
20900     },
20901     
20902     onFocus : function()
20903     {
20904         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20905         this.showPopup();
20906     },
20907     
20908     onBlur : function()
20909     {
20910         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20911         
20912         var d = this.inputEl().getValue();
20913         
20914         this.setValue(d);
20915                 
20916         this.hidePopup();
20917     },
20918     
20919     showPopup : function()
20920     {
20921         this.picker().show();
20922         this.update();
20923         this.place();
20924         
20925         this.fireEvent('showpopup', this, this.date);
20926     },
20927     
20928     hidePopup : function()
20929     {
20930         if(this.isInline) {
20931             return;
20932         }
20933         this.picker().hide();
20934         this.viewMode = this.startViewMode;
20935         this.showMode();
20936         
20937         this.fireEvent('hidepopup', this, this.date);
20938         
20939     },
20940     
20941     onMousedown: function(e)
20942     {
20943         e.stopPropagation();
20944         e.preventDefault();
20945     },
20946     
20947     keyup: function(e)
20948     {
20949         Roo.bootstrap.DateField.superclass.keyup.call(this);
20950         this.update();
20951     },
20952
20953     setValue: function(v)
20954     {
20955         if(this.fireEvent('beforeselect', this, v) !== false){
20956             var d = new Date(this.parseDate(v) ).clearTime();
20957         
20958             if(isNaN(d.getTime())){
20959                 this.date = this.viewDate = '';
20960                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
20961                 return;
20962             }
20963
20964             v = this.formatDate(d);
20965
20966             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
20967
20968             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
20969
20970             this.update();
20971
20972             this.fireEvent('select', this, this.date);
20973         }
20974     },
20975     
20976     getValue: function()
20977     {
20978         return this.formatDate(this.date);
20979     },
20980     
20981     fireKey: function(e)
20982     {
20983         if (!this.picker().isVisible()){
20984             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20985                 this.showPopup();
20986             }
20987             return;
20988         }
20989         
20990         var dateChanged = false,
20991         dir, day, month,
20992         newDate, newViewDate;
20993         
20994         switch(e.keyCode){
20995             case 27: // escape
20996                 this.hidePopup();
20997                 e.preventDefault();
20998                 break;
20999             case 37: // left
21000             case 39: // right
21001                 if (!this.keyboardNavigation) {
21002                     break;
21003                 }
21004                 dir = e.keyCode == 37 ? -1 : 1;
21005                 
21006                 if (e.ctrlKey){
21007                     newDate = this.moveYear(this.date, dir);
21008                     newViewDate = this.moveYear(this.viewDate, dir);
21009                 } else if (e.shiftKey){
21010                     newDate = this.moveMonth(this.date, dir);
21011                     newViewDate = this.moveMonth(this.viewDate, dir);
21012                 } else {
21013                     newDate = new Date(this.date);
21014                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21015                     newViewDate = new Date(this.viewDate);
21016                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21017                 }
21018                 if (this.dateWithinRange(newDate)){
21019                     this.date = newDate;
21020                     this.viewDate = newViewDate;
21021                     this.setValue(this.formatDate(this.date));
21022 //                    this.update();
21023                     e.preventDefault();
21024                     dateChanged = true;
21025                 }
21026                 break;
21027             case 38: // up
21028             case 40: // down
21029                 if (!this.keyboardNavigation) {
21030                     break;
21031                 }
21032                 dir = e.keyCode == 38 ? -1 : 1;
21033                 if (e.ctrlKey){
21034                     newDate = this.moveYear(this.date, dir);
21035                     newViewDate = this.moveYear(this.viewDate, dir);
21036                 } else if (e.shiftKey){
21037                     newDate = this.moveMonth(this.date, dir);
21038                     newViewDate = this.moveMonth(this.viewDate, dir);
21039                 } else {
21040                     newDate = new Date(this.date);
21041                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21042                     newViewDate = new Date(this.viewDate);
21043                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21044                 }
21045                 if (this.dateWithinRange(newDate)){
21046                     this.date = newDate;
21047                     this.viewDate = newViewDate;
21048                     this.setValue(this.formatDate(this.date));
21049 //                    this.update();
21050                     e.preventDefault();
21051                     dateChanged = true;
21052                 }
21053                 break;
21054             case 13: // enter
21055                 this.setValue(this.formatDate(this.date));
21056                 this.hidePopup();
21057                 e.preventDefault();
21058                 break;
21059             case 9: // tab
21060                 this.setValue(this.formatDate(this.date));
21061                 this.hidePopup();
21062                 break;
21063             case 16: // shift
21064             case 17: // ctrl
21065             case 18: // alt
21066                 break;
21067             default :
21068                 this.hidePopup();
21069                 
21070         }
21071     },
21072     
21073     
21074     onClick: function(e) 
21075     {
21076         e.stopPropagation();
21077         e.preventDefault();
21078         
21079         var target = e.getTarget();
21080         
21081         if(target.nodeName.toLowerCase() === 'i'){
21082             target = Roo.get(target).dom.parentNode;
21083         }
21084         
21085         var nodeName = target.nodeName;
21086         var className = target.className;
21087         var html = target.innerHTML;
21088         //Roo.log(nodeName);
21089         
21090         switch(nodeName.toLowerCase()) {
21091             case 'th':
21092                 switch(className) {
21093                     case 'switch':
21094                         this.showMode(1);
21095                         break;
21096                     case 'prev':
21097                     case 'next':
21098                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21099                         switch(this.viewMode){
21100                                 case 0:
21101                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21102                                         break;
21103                                 case 1:
21104                                 case 2:
21105                                         this.viewDate = this.moveYear(this.viewDate, dir);
21106                                         break;
21107                         }
21108                         this.fill();
21109                         break;
21110                     case 'today':
21111                         var date = new Date();
21112                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21113 //                        this.fill()
21114                         this.setValue(this.formatDate(this.date));
21115                         
21116                         this.hidePopup();
21117                         break;
21118                 }
21119                 break;
21120             case 'span':
21121                 if (className.indexOf('disabled') < 0) {
21122                     this.viewDate.setUTCDate(1);
21123                     if (className.indexOf('month') > -1) {
21124                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21125                     } else {
21126                         var year = parseInt(html, 10) || 0;
21127                         this.viewDate.setUTCFullYear(year);
21128                         
21129                     }
21130                     
21131                     if(this.singleMode){
21132                         this.setValue(this.formatDate(this.viewDate));
21133                         this.hidePopup();
21134                         return;
21135                     }
21136                     
21137                     this.showMode(-1);
21138                     this.fill();
21139                 }
21140                 break;
21141                 
21142             case 'td':
21143                 //Roo.log(className);
21144                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21145                     var day = parseInt(html, 10) || 1;
21146                     var year = this.viewDate.getUTCFullYear(),
21147                         month = this.viewDate.getUTCMonth();
21148
21149                     if (className.indexOf('old') > -1) {
21150                         if(month === 0 ){
21151                             month = 11;
21152                             year -= 1;
21153                         }else{
21154                             month -= 1;
21155                         }
21156                     } else if (className.indexOf('new') > -1) {
21157                         if (month == 11) {
21158                             month = 0;
21159                             year += 1;
21160                         } else {
21161                             month += 1;
21162                         }
21163                     }
21164                     //Roo.log([year,month,day]);
21165                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21166                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21167 //                    this.fill();
21168                     //Roo.log(this.formatDate(this.date));
21169                     this.setValue(this.formatDate(this.date));
21170                     this.hidePopup();
21171                 }
21172                 break;
21173         }
21174     },
21175     
21176     setStartDate: function(startDate)
21177     {
21178         this.startDate = startDate || -Infinity;
21179         if (this.startDate !== -Infinity) {
21180             this.startDate = this.parseDate(this.startDate);
21181         }
21182         this.update();
21183         this.updateNavArrows();
21184     },
21185
21186     setEndDate: function(endDate)
21187     {
21188         this.endDate = endDate || Infinity;
21189         if (this.endDate !== Infinity) {
21190             this.endDate = this.parseDate(this.endDate);
21191         }
21192         this.update();
21193         this.updateNavArrows();
21194     },
21195     
21196     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21197     {
21198         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21199         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21200             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21201         }
21202         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21203             return parseInt(d, 10);
21204         });
21205         this.update();
21206         this.updateNavArrows();
21207     },
21208     
21209     updateNavArrows: function() 
21210     {
21211         if(this.singleMode){
21212             return;
21213         }
21214         
21215         var d = new Date(this.viewDate),
21216         year = d.getUTCFullYear(),
21217         month = d.getUTCMonth();
21218         
21219         Roo.each(this.picker().select('.prev', true).elements, function(v){
21220             v.show();
21221             switch (this.viewMode) {
21222                 case 0:
21223
21224                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21225                         v.hide();
21226                     }
21227                     break;
21228                 case 1:
21229                 case 2:
21230                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21231                         v.hide();
21232                     }
21233                     break;
21234             }
21235         });
21236         
21237         Roo.each(this.picker().select('.next', true).elements, function(v){
21238             v.show();
21239             switch (this.viewMode) {
21240                 case 0:
21241
21242                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21243                         v.hide();
21244                     }
21245                     break;
21246                 case 1:
21247                 case 2:
21248                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21249                         v.hide();
21250                     }
21251                     break;
21252             }
21253         })
21254     },
21255     
21256     moveMonth: function(date, dir)
21257     {
21258         if (!dir) {
21259             return date;
21260         }
21261         var new_date = new Date(date.valueOf()),
21262         day = new_date.getUTCDate(),
21263         month = new_date.getUTCMonth(),
21264         mag = Math.abs(dir),
21265         new_month, test;
21266         dir = dir > 0 ? 1 : -1;
21267         if (mag == 1){
21268             test = dir == -1
21269             // If going back one month, make sure month is not current month
21270             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21271             ? function(){
21272                 return new_date.getUTCMonth() == month;
21273             }
21274             // If going forward one month, make sure month is as expected
21275             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21276             : function(){
21277                 return new_date.getUTCMonth() != new_month;
21278             };
21279             new_month = month + dir;
21280             new_date.setUTCMonth(new_month);
21281             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21282             if (new_month < 0 || new_month > 11) {
21283                 new_month = (new_month + 12) % 12;
21284             }
21285         } else {
21286             // For magnitudes >1, move one month at a time...
21287             for (var i=0; i<mag; i++) {
21288                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21289                 new_date = this.moveMonth(new_date, dir);
21290             }
21291             // ...then reset the day, keeping it in the new month
21292             new_month = new_date.getUTCMonth();
21293             new_date.setUTCDate(day);
21294             test = function(){
21295                 return new_month != new_date.getUTCMonth();
21296             };
21297         }
21298         // Common date-resetting loop -- if date is beyond end of month, make it
21299         // end of month
21300         while (test()){
21301             new_date.setUTCDate(--day);
21302             new_date.setUTCMonth(new_month);
21303         }
21304         return new_date;
21305     },
21306
21307     moveYear: function(date, dir)
21308     {
21309         return this.moveMonth(date, dir*12);
21310     },
21311
21312     dateWithinRange: function(date)
21313     {
21314         return date >= this.startDate && date <= this.endDate;
21315     },
21316
21317     
21318     remove: function() 
21319     {
21320         this.picker().remove();
21321     },
21322     
21323     validateValue : function(value)
21324     {
21325         if(this.getVisibilityEl().hasClass('hidden')){
21326             return true;
21327         }
21328         
21329         if(value.length < 1)  {
21330             if(this.allowBlank){
21331                 return true;
21332             }
21333             return false;
21334         }
21335         
21336         if(value.length < this.minLength){
21337             return false;
21338         }
21339         if(value.length > this.maxLength){
21340             return false;
21341         }
21342         if(this.vtype){
21343             var vt = Roo.form.VTypes;
21344             if(!vt[this.vtype](value, this)){
21345                 return false;
21346             }
21347         }
21348         if(typeof this.validator == "function"){
21349             var msg = this.validator(value);
21350             if(msg !== true){
21351                 return false;
21352             }
21353         }
21354         
21355         if(this.regex && !this.regex.test(value)){
21356             return false;
21357         }
21358         
21359         if(typeof(this.parseDate(value)) == 'undefined'){
21360             return false;
21361         }
21362         
21363         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21364             return false;
21365         }      
21366         
21367         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21368             return false;
21369         } 
21370         
21371         
21372         return true;
21373     },
21374     
21375     reset : function()
21376     {
21377         this.date = this.viewDate = '';
21378         
21379         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21380     }
21381    
21382 });
21383
21384 Roo.apply(Roo.bootstrap.DateField,  {
21385     
21386     head : {
21387         tag: 'thead',
21388         cn: [
21389         {
21390             tag: 'tr',
21391             cn: [
21392             {
21393                 tag: 'th',
21394                 cls: 'prev',
21395                 html: '<i class="fa fa-arrow-left"/>'
21396             },
21397             {
21398                 tag: 'th',
21399                 cls: 'switch',
21400                 colspan: '5'
21401             },
21402             {
21403                 tag: 'th',
21404                 cls: 'next',
21405                 html: '<i class="fa fa-arrow-right"/>'
21406             }
21407
21408             ]
21409         }
21410         ]
21411     },
21412     
21413     content : {
21414         tag: 'tbody',
21415         cn: [
21416         {
21417             tag: 'tr',
21418             cn: [
21419             {
21420                 tag: 'td',
21421                 colspan: '7'
21422             }
21423             ]
21424         }
21425         ]
21426     },
21427     
21428     footer : {
21429         tag: 'tfoot',
21430         cn: [
21431         {
21432             tag: 'tr',
21433             cn: [
21434             {
21435                 tag: 'th',
21436                 colspan: '7',
21437                 cls: 'today'
21438             }
21439                     
21440             ]
21441         }
21442         ]
21443     },
21444     
21445     dates:{
21446         en: {
21447             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21448             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21449             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21450             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21451             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21452             today: "Today"
21453         }
21454     },
21455     
21456     modes: [
21457     {
21458         clsName: 'days',
21459         navFnc: 'Month',
21460         navStep: 1
21461     },
21462     {
21463         clsName: 'months',
21464         navFnc: 'FullYear',
21465         navStep: 1
21466     },
21467     {
21468         clsName: 'years',
21469         navFnc: 'FullYear',
21470         navStep: 10
21471     }]
21472 });
21473
21474 Roo.apply(Roo.bootstrap.DateField,  {
21475   
21476     template : {
21477         tag: 'div',
21478         cls: 'datepicker dropdown-menu roo-dynamic',
21479         cn: [
21480         {
21481             tag: 'div',
21482             cls: 'datepicker-days',
21483             cn: [
21484             {
21485                 tag: 'table',
21486                 cls: 'table-condensed',
21487                 cn:[
21488                 Roo.bootstrap.DateField.head,
21489                 {
21490                     tag: 'tbody'
21491                 },
21492                 Roo.bootstrap.DateField.footer
21493                 ]
21494             }
21495             ]
21496         },
21497         {
21498             tag: 'div',
21499             cls: 'datepicker-months',
21500             cn: [
21501             {
21502                 tag: 'table',
21503                 cls: 'table-condensed',
21504                 cn:[
21505                 Roo.bootstrap.DateField.head,
21506                 Roo.bootstrap.DateField.content,
21507                 Roo.bootstrap.DateField.footer
21508                 ]
21509             }
21510             ]
21511         },
21512         {
21513             tag: 'div',
21514             cls: 'datepicker-years',
21515             cn: [
21516             {
21517                 tag: 'table',
21518                 cls: 'table-condensed',
21519                 cn:[
21520                 Roo.bootstrap.DateField.head,
21521                 Roo.bootstrap.DateField.content,
21522                 Roo.bootstrap.DateField.footer
21523                 ]
21524             }
21525             ]
21526         }
21527         ]
21528     }
21529 });
21530
21531  
21532
21533  /*
21534  * - LGPL
21535  *
21536  * TimeField
21537  * 
21538  */
21539
21540 /**
21541  * @class Roo.bootstrap.TimeField
21542  * @extends Roo.bootstrap.Input
21543  * Bootstrap DateField class
21544  * 
21545  * 
21546  * @constructor
21547  * Create a new TimeField
21548  * @param {Object} config The config object
21549  */
21550
21551 Roo.bootstrap.TimeField = function(config){
21552     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21553     this.addEvents({
21554             /**
21555              * @event show
21556              * Fires when this field show.
21557              * @param {Roo.bootstrap.DateField} thisthis
21558              * @param {Mixed} date The date value
21559              */
21560             show : true,
21561             /**
21562              * @event show
21563              * Fires when this field hide.
21564              * @param {Roo.bootstrap.DateField} this
21565              * @param {Mixed} date The date value
21566              */
21567             hide : true,
21568             /**
21569              * @event select
21570              * Fires when select a date.
21571              * @param {Roo.bootstrap.DateField} this
21572              * @param {Mixed} date The date value
21573              */
21574             select : true
21575         });
21576 };
21577
21578 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21579     
21580     /**
21581      * @cfg {String} format
21582      * The default time format string which can be overriden for localization support.  The format must be
21583      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21584      */
21585     format : "H:i",
21586        
21587     onRender: function(ct, position)
21588     {
21589         
21590         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21591                 
21592         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21593         
21594         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21595         
21596         this.pop = this.picker().select('>.datepicker-time',true).first();
21597         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21598         
21599         this.picker().on('mousedown', this.onMousedown, this);
21600         this.picker().on('click', this.onClick, this);
21601         
21602         this.picker().addClass('datepicker-dropdown');
21603     
21604         this.fillTime();
21605         this.update();
21606             
21607         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21608         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21609         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21610         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21611         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21612         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21613
21614     },
21615     
21616     fireKey: function(e){
21617         if (!this.picker().isVisible()){
21618             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21619                 this.show();
21620             }
21621             return;
21622         }
21623
21624         e.preventDefault();
21625         
21626         switch(e.keyCode){
21627             case 27: // escape
21628                 this.hide();
21629                 break;
21630             case 37: // left
21631             case 39: // right
21632                 this.onTogglePeriod();
21633                 break;
21634             case 38: // up
21635                 this.onIncrementMinutes();
21636                 break;
21637             case 40: // down
21638                 this.onDecrementMinutes();
21639                 break;
21640             case 13: // enter
21641             case 9: // tab
21642                 this.setTime();
21643                 break;
21644         }
21645     },
21646     
21647     onClick: function(e) {
21648         e.stopPropagation();
21649         e.preventDefault();
21650     },
21651     
21652     picker : function()
21653     {
21654         return this.el.select('.datepicker', true).first();
21655     },
21656     
21657     fillTime: function()
21658     {    
21659         var time = this.pop.select('tbody', true).first();
21660         
21661         time.dom.innerHTML = '';
21662         
21663         time.createChild({
21664             tag: 'tr',
21665             cn: [
21666                 {
21667                     tag: 'td',
21668                     cn: [
21669                         {
21670                             tag: 'a',
21671                             href: '#',
21672                             cls: 'btn',
21673                             cn: [
21674                                 {
21675                                     tag: 'span',
21676                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21677                                 }
21678                             ]
21679                         } 
21680                     ]
21681                 },
21682                 {
21683                     tag: 'td',
21684                     cls: 'separator'
21685                 },
21686                 {
21687                     tag: 'td',
21688                     cn: [
21689                         {
21690                             tag: 'a',
21691                             href: '#',
21692                             cls: 'btn',
21693                             cn: [
21694                                 {
21695                                     tag: 'span',
21696                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21697                                 }
21698                             ]
21699                         }
21700                     ]
21701                 },
21702                 {
21703                     tag: 'td',
21704                     cls: 'separator'
21705                 }
21706             ]
21707         });
21708         
21709         time.createChild({
21710             tag: 'tr',
21711             cn: [
21712                 {
21713                     tag: 'td',
21714                     cn: [
21715                         {
21716                             tag: 'span',
21717                             cls: 'timepicker-hour',
21718                             html: '00'
21719                         }  
21720                     ]
21721                 },
21722                 {
21723                     tag: 'td',
21724                     cls: 'separator',
21725                     html: ':'
21726                 },
21727                 {
21728                     tag: 'td',
21729                     cn: [
21730                         {
21731                             tag: 'span',
21732                             cls: 'timepicker-minute',
21733                             html: '00'
21734                         }  
21735                     ]
21736                 },
21737                 {
21738                     tag: 'td',
21739                     cls: 'separator'
21740                 },
21741                 {
21742                     tag: 'td',
21743                     cn: [
21744                         {
21745                             tag: 'button',
21746                             type: 'button',
21747                             cls: 'btn btn-primary period',
21748                             html: 'AM'
21749                             
21750                         }
21751                     ]
21752                 }
21753             ]
21754         });
21755         
21756         time.createChild({
21757             tag: 'tr',
21758             cn: [
21759                 {
21760                     tag: 'td',
21761                     cn: [
21762                         {
21763                             tag: 'a',
21764                             href: '#',
21765                             cls: 'btn',
21766                             cn: [
21767                                 {
21768                                     tag: 'span',
21769                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21770                                 }
21771                             ]
21772                         }
21773                     ]
21774                 },
21775                 {
21776                     tag: 'td',
21777                     cls: 'separator'
21778                 },
21779                 {
21780                     tag: 'td',
21781                     cn: [
21782                         {
21783                             tag: 'a',
21784                             href: '#',
21785                             cls: 'btn',
21786                             cn: [
21787                                 {
21788                                     tag: 'span',
21789                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21790                                 }
21791                             ]
21792                         }
21793                     ]
21794                 },
21795                 {
21796                     tag: 'td',
21797                     cls: 'separator'
21798                 }
21799             ]
21800         });
21801         
21802     },
21803     
21804     update: function()
21805     {
21806         
21807         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21808         
21809         this.fill();
21810     },
21811     
21812     fill: function() 
21813     {
21814         var hours = this.time.getHours();
21815         var minutes = this.time.getMinutes();
21816         var period = 'AM';
21817         
21818         if(hours > 11){
21819             period = 'PM';
21820         }
21821         
21822         if(hours == 0){
21823             hours = 12;
21824         }
21825         
21826         
21827         if(hours > 12){
21828             hours = hours - 12;
21829         }
21830         
21831         if(hours < 10){
21832             hours = '0' + hours;
21833         }
21834         
21835         if(minutes < 10){
21836             minutes = '0' + minutes;
21837         }
21838         
21839         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21840         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21841         this.pop.select('button', true).first().dom.innerHTML = period;
21842         
21843     },
21844     
21845     place: function()
21846     {   
21847         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21848         
21849         var cls = ['bottom'];
21850         
21851         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21852             cls.pop();
21853             cls.push('top');
21854         }
21855         
21856         cls.push('right');
21857         
21858         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21859             cls.pop();
21860             cls.push('left');
21861         }
21862         
21863         this.picker().addClass(cls.join('-'));
21864         
21865         var _this = this;
21866         
21867         Roo.each(cls, function(c){
21868             if(c == 'bottom'){
21869                 _this.picker().setTop(_this.inputEl().getHeight());
21870                 return;
21871             }
21872             if(c == 'top'){
21873                 _this.picker().setTop(0 - _this.picker().getHeight());
21874                 return;
21875             }
21876             
21877             if(c == 'left'){
21878                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21879                 return;
21880             }
21881             if(c == 'right'){
21882                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21883                 return;
21884             }
21885         });
21886         
21887     },
21888   
21889     onFocus : function()
21890     {
21891         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21892         this.show();
21893     },
21894     
21895     onBlur : function()
21896     {
21897         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21898         this.hide();
21899     },
21900     
21901     show : function()
21902     {
21903         this.picker().show();
21904         this.pop.show();
21905         this.update();
21906         this.place();
21907         
21908         this.fireEvent('show', this, this.date);
21909     },
21910     
21911     hide : function()
21912     {
21913         this.picker().hide();
21914         this.pop.hide();
21915         
21916         this.fireEvent('hide', this, this.date);
21917     },
21918     
21919     setTime : function()
21920     {
21921         this.hide();
21922         this.setValue(this.time.format(this.format));
21923         
21924         this.fireEvent('select', this, this.date);
21925         
21926         
21927     },
21928     
21929     onMousedown: function(e){
21930         e.stopPropagation();
21931         e.preventDefault();
21932     },
21933     
21934     onIncrementHours: function()
21935     {
21936         Roo.log('onIncrementHours');
21937         this.time = this.time.add(Date.HOUR, 1);
21938         this.update();
21939         
21940     },
21941     
21942     onDecrementHours: function()
21943     {
21944         Roo.log('onDecrementHours');
21945         this.time = this.time.add(Date.HOUR, -1);
21946         this.update();
21947     },
21948     
21949     onIncrementMinutes: function()
21950     {
21951         Roo.log('onIncrementMinutes');
21952         this.time = this.time.add(Date.MINUTE, 1);
21953         this.update();
21954     },
21955     
21956     onDecrementMinutes: function()
21957     {
21958         Roo.log('onDecrementMinutes');
21959         this.time = this.time.add(Date.MINUTE, -1);
21960         this.update();
21961     },
21962     
21963     onTogglePeriod: function()
21964     {
21965         Roo.log('onTogglePeriod');
21966         this.time = this.time.add(Date.HOUR, 12);
21967         this.update();
21968     }
21969     
21970    
21971 });
21972
21973 Roo.apply(Roo.bootstrap.TimeField,  {
21974     
21975     content : {
21976         tag: 'tbody',
21977         cn: [
21978             {
21979                 tag: 'tr',
21980                 cn: [
21981                 {
21982                     tag: 'td',
21983                     colspan: '7'
21984                 }
21985                 ]
21986             }
21987         ]
21988     },
21989     
21990     footer : {
21991         tag: 'tfoot',
21992         cn: [
21993             {
21994                 tag: 'tr',
21995                 cn: [
21996                 {
21997                     tag: 'th',
21998                     colspan: '7',
21999                     cls: '',
22000                     cn: [
22001                         {
22002                             tag: 'button',
22003                             cls: 'btn btn-info ok',
22004                             html: 'OK'
22005                         }
22006                     ]
22007                 }
22008
22009                 ]
22010             }
22011         ]
22012     }
22013 });
22014
22015 Roo.apply(Roo.bootstrap.TimeField,  {
22016   
22017     template : {
22018         tag: 'div',
22019         cls: 'datepicker dropdown-menu',
22020         cn: [
22021             {
22022                 tag: 'div',
22023                 cls: 'datepicker-time',
22024                 cn: [
22025                 {
22026                     tag: 'table',
22027                     cls: 'table-condensed',
22028                     cn:[
22029                     Roo.bootstrap.TimeField.content,
22030                     Roo.bootstrap.TimeField.footer
22031                     ]
22032                 }
22033                 ]
22034             }
22035         ]
22036     }
22037 });
22038
22039  
22040
22041  /*
22042  * - LGPL
22043  *
22044  * MonthField
22045  * 
22046  */
22047
22048 /**
22049  * @class Roo.bootstrap.MonthField
22050  * @extends Roo.bootstrap.Input
22051  * Bootstrap MonthField class
22052  * 
22053  * @cfg {String} language default en
22054  * 
22055  * @constructor
22056  * Create a new MonthField
22057  * @param {Object} config The config object
22058  */
22059
22060 Roo.bootstrap.MonthField = function(config){
22061     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22062     
22063     this.addEvents({
22064         /**
22065          * @event show
22066          * Fires when this field show.
22067          * @param {Roo.bootstrap.MonthField} this
22068          * @param {Mixed} date The date value
22069          */
22070         show : true,
22071         /**
22072          * @event show
22073          * Fires when this field hide.
22074          * @param {Roo.bootstrap.MonthField} this
22075          * @param {Mixed} date The date value
22076          */
22077         hide : true,
22078         /**
22079          * @event select
22080          * Fires when select a date.
22081          * @param {Roo.bootstrap.MonthField} this
22082          * @param {String} oldvalue The old value
22083          * @param {String} newvalue The new value
22084          */
22085         select : true
22086     });
22087 };
22088
22089 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22090     
22091     onRender: function(ct, position)
22092     {
22093         
22094         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22095         
22096         this.language = this.language || 'en';
22097         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22098         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22099         
22100         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22101         this.isInline = false;
22102         this.isInput = true;
22103         this.component = this.el.select('.add-on', true).first() || false;
22104         this.component = (this.component && this.component.length === 0) ? false : this.component;
22105         this.hasInput = this.component && this.inputEL().length;
22106         
22107         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22108         
22109         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22110         
22111         this.picker().on('mousedown', this.onMousedown, this);
22112         this.picker().on('click', this.onClick, this);
22113         
22114         this.picker().addClass('datepicker-dropdown');
22115         
22116         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22117             v.setStyle('width', '189px');
22118         });
22119         
22120         this.fillMonths();
22121         
22122         this.update();
22123         
22124         if(this.isInline) {
22125             this.show();
22126         }
22127         
22128     },
22129     
22130     setValue: function(v, suppressEvent)
22131     {   
22132         var o = this.getValue();
22133         
22134         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22135         
22136         this.update();
22137
22138         if(suppressEvent !== true){
22139             this.fireEvent('select', this, o, v);
22140         }
22141         
22142     },
22143     
22144     getValue: function()
22145     {
22146         return this.value;
22147     },
22148     
22149     onClick: function(e) 
22150     {
22151         e.stopPropagation();
22152         e.preventDefault();
22153         
22154         var target = e.getTarget();
22155         
22156         if(target.nodeName.toLowerCase() === 'i'){
22157             target = Roo.get(target).dom.parentNode;
22158         }
22159         
22160         var nodeName = target.nodeName;
22161         var className = target.className;
22162         var html = target.innerHTML;
22163         
22164         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22165             return;
22166         }
22167         
22168         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22169         
22170         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22171         
22172         this.hide();
22173                         
22174     },
22175     
22176     picker : function()
22177     {
22178         return this.pickerEl;
22179     },
22180     
22181     fillMonths: function()
22182     {    
22183         var i = 0;
22184         var months = this.picker().select('>.datepicker-months td', true).first();
22185         
22186         months.dom.innerHTML = '';
22187         
22188         while (i < 12) {
22189             var month = {
22190                 tag: 'span',
22191                 cls: 'month',
22192                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22193             };
22194             
22195             months.createChild(month);
22196         }
22197         
22198     },
22199     
22200     update: function()
22201     {
22202         var _this = this;
22203         
22204         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22205             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22206         }
22207         
22208         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22209             e.removeClass('active');
22210             
22211             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22212                 e.addClass('active');
22213             }
22214         })
22215     },
22216     
22217     place: function()
22218     {
22219         if(this.isInline) {
22220             return;
22221         }
22222         
22223         this.picker().removeClass(['bottom', 'top']);
22224         
22225         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22226             /*
22227              * place to the top of element!
22228              *
22229              */
22230             
22231             this.picker().addClass('top');
22232             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22233             
22234             return;
22235         }
22236         
22237         this.picker().addClass('bottom');
22238         
22239         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22240     },
22241     
22242     onFocus : function()
22243     {
22244         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22245         this.show();
22246     },
22247     
22248     onBlur : function()
22249     {
22250         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22251         
22252         var d = this.inputEl().getValue();
22253         
22254         this.setValue(d);
22255                 
22256         this.hide();
22257     },
22258     
22259     show : function()
22260     {
22261         this.picker().show();
22262         this.picker().select('>.datepicker-months', true).first().show();
22263         this.update();
22264         this.place();
22265         
22266         this.fireEvent('show', this, this.date);
22267     },
22268     
22269     hide : function()
22270     {
22271         if(this.isInline) {
22272             return;
22273         }
22274         this.picker().hide();
22275         this.fireEvent('hide', this, this.date);
22276         
22277     },
22278     
22279     onMousedown: function(e)
22280     {
22281         e.stopPropagation();
22282         e.preventDefault();
22283     },
22284     
22285     keyup: function(e)
22286     {
22287         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22288         this.update();
22289     },
22290
22291     fireKey: function(e)
22292     {
22293         if (!this.picker().isVisible()){
22294             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22295                 this.show();
22296             }
22297             return;
22298         }
22299         
22300         var dir;
22301         
22302         switch(e.keyCode){
22303             case 27: // escape
22304                 this.hide();
22305                 e.preventDefault();
22306                 break;
22307             case 37: // left
22308             case 39: // right
22309                 dir = e.keyCode == 37 ? -1 : 1;
22310                 
22311                 this.vIndex = this.vIndex + dir;
22312                 
22313                 if(this.vIndex < 0){
22314                     this.vIndex = 0;
22315                 }
22316                 
22317                 if(this.vIndex > 11){
22318                     this.vIndex = 11;
22319                 }
22320                 
22321                 if(isNaN(this.vIndex)){
22322                     this.vIndex = 0;
22323                 }
22324                 
22325                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22326                 
22327                 break;
22328             case 38: // up
22329             case 40: // down
22330                 
22331                 dir = e.keyCode == 38 ? -1 : 1;
22332                 
22333                 this.vIndex = this.vIndex + dir * 4;
22334                 
22335                 if(this.vIndex < 0){
22336                     this.vIndex = 0;
22337                 }
22338                 
22339                 if(this.vIndex > 11){
22340                     this.vIndex = 11;
22341                 }
22342                 
22343                 if(isNaN(this.vIndex)){
22344                     this.vIndex = 0;
22345                 }
22346                 
22347                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22348                 break;
22349                 
22350             case 13: // enter
22351                 
22352                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22353                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22354                 }
22355                 
22356                 this.hide();
22357                 e.preventDefault();
22358                 break;
22359             case 9: // tab
22360                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22361                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22362                 }
22363                 this.hide();
22364                 break;
22365             case 16: // shift
22366             case 17: // ctrl
22367             case 18: // alt
22368                 break;
22369             default :
22370                 this.hide();
22371                 
22372         }
22373     },
22374     
22375     remove: function() 
22376     {
22377         this.picker().remove();
22378     }
22379    
22380 });
22381
22382 Roo.apply(Roo.bootstrap.MonthField,  {
22383     
22384     content : {
22385         tag: 'tbody',
22386         cn: [
22387         {
22388             tag: 'tr',
22389             cn: [
22390             {
22391                 tag: 'td',
22392                 colspan: '7'
22393             }
22394             ]
22395         }
22396         ]
22397     },
22398     
22399     dates:{
22400         en: {
22401             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22402             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22403         }
22404     }
22405 });
22406
22407 Roo.apply(Roo.bootstrap.MonthField,  {
22408   
22409     template : {
22410         tag: 'div',
22411         cls: 'datepicker dropdown-menu roo-dynamic',
22412         cn: [
22413             {
22414                 tag: 'div',
22415                 cls: 'datepicker-months',
22416                 cn: [
22417                 {
22418                     tag: 'table',
22419                     cls: 'table-condensed',
22420                     cn:[
22421                         Roo.bootstrap.DateField.content
22422                     ]
22423                 }
22424                 ]
22425             }
22426         ]
22427     }
22428 });
22429
22430  
22431
22432  
22433  /*
22434  * - LGPL
22435  *
22436  * CheckBox
22437  * 
22438  */
22439
22440 /**
22441  * @class Roo.bootstrap.CheckBox
22442  * @extends Roo.bootstrap.Input
22443  * Bootstrap CheckBox class
22444  * 
22445  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22446  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22447  * @cfg {String} boxLabel The text that appears beside the checkbox
22448  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22449  * @cfg {Boolean} checked initnal the element
22450  * @cfg {Boolean} inline inline the element (default false)
22451  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22452  * @cfg {String} tooltip label tooltip
22453  * 
22454  * @constructor
22455  * Create a new CheckBox
22456  * @param {Object} config The config object
22457  */
22458
22459 Roo.bootstrap.CheckBox = function(config){
22460     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22461    
22462     this.addEvents({
22463         /**
22464         * @event check
22465         * Fires when the element is checked or unchecked.
22466         * @param {Roo.bootstrap.CheckBox} this This input
22467         * @param {Boolean} checked The new checked value
22468         */
22469        check : true,
22470        /**
22471         * @event click
22472         * Fires when the element is click.
22473         * @param {Roo.bootstrap.CheckBox} this This input
22474         */
22475        click : true
22476     });
22477     
22478 };
22479
22480 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22481   
22482     inputType: 'checkbox',
22483     inputValue: 1,
22484     valueOff: 0,
22485     boxLabel: false,
22486     checked: false,
22487     weight : false,
22488     inline: false,
22489     tooltip : '',
22490     
22491     // checkbox success does not make any sense really.. 
22492     invalidClass : "",
22493     validClass : "",
22494     
22495     
22496     getAutoCreate : function()
22497     {
22498         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22499         
22500         var id = Roo.id();
22501         
22502         var cfg = {};
22503         
22504         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22505         
22506         if(this.inline){
22507             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22508         }
22509         
22510         var input =  {
22511             tag: 'input',
22512             id : id,
22513             type : this.inputType,
22514             value : this.inputValue,
22515             cls : 'roo-' + this.inputType, //'form-box',
22516             placeholder : this.placeholder || ''
22517             
22518         };
22519         
22520         if(this.inputType != 'radio'){
22521             var hidden =  {
22522                 tag: 'input',
22523                 type : 'hidden',
22524                 cls : 'roo-hidden-value',
22525                 value : this.checked ? this.inputValue : this.valueOff
22526             };
22527         }
22528         
22529             
22530         if (this.weight) { // Validity check?
22531             cfg.cls += " " + this.inputType + "-" + this.weight;
22532         }
22533         
22534         if (this.disabled) {
22535             input.disabled=true;
22536         }
22537         
22538         if(this.checked){
22539             input.checked = this.checked;
22540         }
22541         
22542         if (this.name) {
22543             
22544             input.name = this.name;
22545             
22546             if(this.inputType != 'radio'){
22547                 hidden.name = this.name;
22548                 input.name = '_hidden_' + this.name;
22549             }
22550         }
22551         
22552         if (this.size) {
22553             input.cls += ' input-' + this.size;
22554         }
22555         
22556         var settings=this;
22557         
22558         ['xs','sm','md','lg'].map(function(size){
22559             if (settings[size]) {
22560                 cfg.cls += ' col-' + size + '-' + settings[size];
22561             }
22562         });
22563         
22564         var inputblock = input;
22565          
22566         if (this.before || this.after) {
22567             
22568             inputblock = {
22569                 cls : 'input-group',
22570                 cn :  [] 
22571             };
22572             
22573             if (this.before) {
22574                 inputblock.cn.push({
22575                     tag :'span',
22576                     cls : 'input-group-addon',
22577                     html : this.before
22578                 });
22579             }
22580             
22581             inputblock.cn.push(input);
22582             
22583             if(this.inputType != 'radio'){
22584                 inputblock.cn.push(hidden);
22585             }
22586             
22587             if (this.after) {
22588                 inputblock.cn.push({
22589                     tag :'span',
22590                     cls : 'input-group-addon',
22591                     html : this.after
22592                 });
22593             }
22594             
22595         }
22596         var boxLabelCfg = false;
22597         
22598         if(this.boxLabel){
22599            
22600             boxLabelCfg = {
22601                 tag: 'label',
22602                 //'for': id, // box label is handled by onclick - so no for...
22603                 cls: 'box-label',
22604                 html: this.boxLabel
22605             };
22606             if(this.tooltip){
22607                 boxLabelCfg.tooltip = this.tooltip;
22608             }
22609              
22610         }
22611         
22612         
22613         if (align ==='left' && this.fieldLabel.length) {
22614 //                Roo.log("left and has label");
22615             cfg.cn = [
22616                 {
22617                     tag: 'label',
22618                     'for' :  id,
22619                     cls : 'control-label',
22620                     html : this.fieldLabel
22621                 },
22622                 {
22623                     cls : "", 
22624                     cn: [
22625                         inputblock
22626                     ]
22627                 }
22628             ];
22629             
22630             if (boxLabelCfg) {
22631                 cfg.cn[1].cn.push(boxLabelCfg);
22632             }
22633             
22634             if(this.labelWidth > 12){
22635                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22636             }
22637             
22638             if(this.labelWidth < 13 && this.labelmd == 0){
22639                 this.labelmd = this.labelWidth;
22640             }
22641             
22642             if(this.labellg > 0){
22643                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22644                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22645             }
22646             
22647             if(this.labelmd > 0){
22648                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22649                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22650             }
22651             
22652             if(this.labelsm > 0){
22653                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22654                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22655             }
22656             
22657             if(this.labelxs > 0){
22658                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22659                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22660             }
22661             
22662         } else if ( this.fieldLabel.length) {
22663 //                Roo.log(" label");
22664                 cfg.cn = [
22665                    
22666                     {
22667                         tag: this.boxLabel ? 'span' : 'label',
22668                         'for': id,
22669                         cls: 'control-label box-input-label',
22670                         //cls : 'input-group-addon',
22671                         html : this.fieldLabel
22672                     },
22673                     
22674                     inputblock
22675                     
22676                 ];
22677                 if (boxLabelCfg) {
22678                     cfg.cn.push(boxLabelCfg);
22679                 }
22680
22681         } else {
22682             
22683 //                Roo.log(" no label && no align");
22684                 cfg.cn = [  inputblock ] ;
22685                 if (boxLabelCfg) {
22686                     cfg.cn.push(boxLabelCfg);
22687                 }
22688
22689                 
22690         }
22691         
22692        
22693         
22694         if(this.inputType != 'radio'){
22695             cfg.cn.push(hidden);
22696         }
22697         
22698         return cfg;
22699         
22700     },
22701     
22702     /**
22703      * return the real input element.
22704      */
22705     inputEl: function ()
22706     {
22707         return this.el.select('input.roo-' + this.inputType,true).first();
22708     },
22709     hiddenEl: function ()
22710     {
22711         return this.el.select('input.roo-hidden-value',true).first();
22712     },
22713     
22714     labelEl: function()
22715     {
22716         return this.el.select('label.control-label',true).first();
22717     },
22718     /* depricated... */
22719     
22720     label: function()
22721     {
22722         return this.labelEl();
22723     },
22724     
22725     boxLabelEl: function()
22726     {
22727         return this.el.select('label.box-label',true).first();
22728     },
22729     
22730     initEvents : function()
22731     {
22732 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22733         
22734         this.inputEl().on('click', this.onClick,  this);
22735         
22736         if (this.boxLabel) { 
22737             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22738         }
22739         
22740         this.startValue = this.getValue();
22741         
22742         if(this.groupId){
22743             Roo.bootstrap.CheckBox.register(this);
22744         }
22745     },
22746     
22747     onClick : function(e)
22748     {   
22749         if(this.fireEvent('click', this, e) !== false){
22750             this.setChecked(!this.checked);
22751         }
22752         
22753     },
22754     
22755     setChecked : function(state,suppressEvent)
22756     {
22757         this.startValue = this.getValue();
22758
22759         if(this.inputType == 'radio'){
22760             
22761             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22762                 e.dom.checked = false;
22763             });
22764             
22765             this.inputEl().dom.checked = true;
22766             
22767             this.inputEl().dom.value = this.inputValue;
22768             
22769             if(suppressEvent !== true){
22770                 this.fireEvent('check', this, true);
22771             }
22772             
22773             this.validate();
22774             
22775             return;
22776         }
22777         
22778         this.checked = state;
22779         
22780         this.inputEl().dom.checked = state;
22781         
22782         
22783         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22784         
22785         if(suppressEvent !== true){
22786             this.fireEvent('check', this, state);
22787         }
22788         
22789         this.validate();
22790     },
22791     
22792     getValue : function()
22793     {
22794         if(this.inputType == 'radio'){
22795             return this.getGroupValue();
22796         }
22797         
22798         return this.hiddenEl().dom.value;
22799         
22800     },
22801     
22802     getGroupValue : function()
22803     {
22804         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22805             return '';
22806         }
22807         
22808         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22809     },
22810     
22811     setValue : function(v,suppressEvent)
22812     {
22813         if(this.inputType == 'radio'){
22814             this.setGroupValue(v, suppressEvent);
22815             return;
22816         }
22817         
22818         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22819         
22820         this.validate();
22821     },
22822     
22823     setGroupValue : function(v, suppressEvent)
22824     {
22825         this.startValue = this.getValue();
22826         
22827         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22828             e.dom.checked = false;
22829             
22830             if(e.dom.value == v){
22831                 e.dom.checked = true;
22832             }
22833         });
22834         
22835         if(suppressEvent !== true){
22836             this.fireEvent('check', this, true);
22837         }
22838
22839         this.validate();
22840         
22841         return;
22842     },
22843     
22844     validate : function()
22845     {
22846         if(this.getVisibilityEl().hasClass('hidden')){
22847             return true;
22848         }
22849         
22850         if(
22851                 this.disabled || 
22852                 (this.inputType == 'radio' && this.validateRadio()) ||
22853                 (this.inputType == 'checkbox' && this.validateCheckbox())
22854         ){
22855             this.markValid();
22856             return true;
22857         }
22858         
22859         this.markInvalid();
22860         return false;
22861     },
22862     
22863     validateRadio : function()
22864     {
22865         if(this.getVisibilityEl().hasClass('hidden')){
22866             return true;
22867         }
22868         
22869         if(this.allowBlank){
22870             return true;
22871         }
22872         
22873         var valid = false;
22874         
22875         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22876             if(!e.dom.checked){
22877                 return;
22878             }
22879             
22880             valid = true;
22881             
22882             return false;
22883         });
22884         
22885         return valid;
22886     },
22887     
22888     validateCheckbox : function()
22889     {
22890         if(!this.groupId){
22891             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22892             //return (this.getValue() == this.inputValue) ? true : false;
22893         }
22894         
22895         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22896         
22897         if(!group){
22898             return false;
22899         }
22900         
22901         var r = false;
22902         
22903         for(var i in group){
22904             if(group[i].el.isVisible(true)){
22905                 r = false;
22906                 break;
22907             }
22908             
22909             r = true;
22910         }
22911         
22912         for(var i in group){
22913             if(r){
22914                 break;
22915             }
22916             
22917             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22918         }
22919         
22920         return r;
22921     },
22922     
22923     /**
22924      * Mark this field as valid
22925      */
22926     markValid : function()
22927     {
22928         var _this = this;
22929         
22930         this.fireEvent('valid', this);
22931         
22932         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22933         
22934         if(this.groupId){
22935             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22936         }
22937         
22938         if(label){
22939             label.markValid();
22940         }
22941
22942         if(this.inputType == 'radio'){
22943             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22944                 var fg = e.findParent('.form-group', false, true);
22945                 if (Roo.bootstrap.version == 3) {
22946                     fg.removeClass([_this.invalidClass, _this.validClass]);
22947                     fg.addClass(_this.validClass);
22948                 } else {
22949                     fg.removeClass(['is-valid', 'is-invalid']);
22950                     fg.addClass('is-valid');
22951                 }
22952             });
22953             
22954             return;
22955         }
22956
22957         if(!this.groupId){
22958             var fg = this.el.findParent('.form-group', false, true);
22959             if (Roo.bootstrap.version == 3) {
22960                 fg.removeClass([this.invalidClass, this.validClass]);
22961                 fg.addClass(this.validClass);
22962             } else {
22963                 fg.removeClass(['is-valid', 'is-invalid']);
22964                 fg.addClass('is-valid');
22965             }
22966             return;
22967         }
22968         
22969         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22970         
22971         if(!group){
22972             return;
22973         }
22974         
22975         for(var i in group){
22976             var fg = group[i].el.findParent('.form-group', false, true);
22977             if (Roo.bootstrap.version == 3) {
22978                 fg.removeClass([this.invalidClass, this.validClass]);
22979                 fg.addClass(this.validClass);
22980             } else {
22981                 fg.removeClass(['is-valid', 'is-invalid']);
22982                 fg.addClass('is-valid');
22983             }
22984         }
22985     },
22986     
22987      /**
22988      * Mark this field as invalid
22989      * @param {String} msg The validation message
22990      */
22991     markInvalid : function(msg)
22992     {
22993         if(this.allowBlank){
22994             return;
22995         }
22996         
22997         var _this = this;
22998         
22999         this.fireEvent('invalid', this, msg);
23000         
23001         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23002         
23003         if(this.groupId){
23004             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23005         }
23006         
23007         if(label){
23008             label.markInvalid();
23009         }
23010             
23011         if(this.inputType == 'radio'){
23012             
23013             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23014                 var fg = e.findParent('.form-group', false, true);
23015                 if (Roo.bootstrap.version == 3) {
23016                     fg.removeClass([_this.invalidClass, _this.validClass]);
23017                     fg.addClass(_this.invalidClass);
23018                 } else {
23019                     fg.removeClass(['is-invalid', 'is-valid']);
23020                     fg.addClass('is-invalid');
23021                 }
23022             });
23023             
23024             return;
23025         }
23026         
23027         if(!this.groupId){
23028             var fg = this.el.findParent('.form-group', false, true);
23029             if (Roo.bootstrap.version == 3) {
23030                 fg.removeClass([_this.invalidClass, _this.validClass]);
23031                 fg.addClass(_this.invalidClass);
23032             } else {
23033                 fg.removeClass(['is-invalid', 'is-valid']);
23034                 fg.addClass('is-invalid');
23035             }
23036             return;
23037         }
23038         
23039         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23040         
23041         if(!group){
23042             return;
23043         }
23044         
23045         for(var i in group){
23046             var fg = group[i].el.findParent('.form-group', false, true);
23047             if (Roo.bootstrap.version == 3) {
23048                 fg.removeClass([_this.invalidClass, _this.validClass]);
23049                 fg.addClass(_this.invalidClass);
23050             } else {
23051                 fg.removeClass(['is-invalid', 'is-valid']);
23052                 fg.addClass('is-invalid');
23053             }
23054         }
23055         
23056     },
23057     
23058     clearInvalid : function()
23059     {
23060         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23061         
23062         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23063         
23064         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23065         
23066         if (label && label.iconEl) {
23067             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23068             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23069         }
23070     },
23071     
23072     disable : function()
23073     {
23074         if(this.inputType != 'radio'){
23075             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23076             return;
23077         }
23078         
23079         var _this = this;
23080         
23081         if(this.rendered){
23082             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23083                 _this.getActionEl().addClass(this.disabledClass);
23084                 e.dom.disabled = true;
23085             });
23086         }
23087         
23088         this.disabled = true;
23089         this.fireEvent("disable", this);
23090         return this;
23091     },
23092
23093     enable : function()
23094     {
23095         if(this.inputType != 'radio'){
23096             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23097             return;
23098         }
23099         
23100         var _this = this;
23101         
23102         if(this.rendered){
23103             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23104                 _this.getActionEl().removeClass(this.disabledClass);
23105                 e.dom.disabled = false;
23106             });
23107         }
23108         
23109         this.disabled = false;
23110         this.fireEvent("enable", this);
23111         return this;
23112     },
23113     
23114     setBoxLabel : function(v)
23115     {
23116         this.boxLabel = v;
23117         
23118         if(this.rendered){
23119             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23120         }
23121     }
23122
23123 });
23124
23125 Roo.apply(Roo.bootstrap.CheckBox, {
23126     
23127     groups: {},
23128     
23129      /**
23130     * register a CheckBox Group
23131     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23132     */
23133     register : function(checkbox)
23134     {
23135         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23136             this.groups[checkbox.groupId] = {};
23137         }
23138         
23139         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23140             return;
23141         }
23142         
23143         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23144         
23145     },
23146     /**
23147     * fetch a CheckBox Group based on the group ID
23148     * @param {string} the group ID
23149     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23150     */
23151     get: function(groupId) {
23152         if (typeof(this.groups[groupId]) == 'undefined') {
23153             return false;
23154         }
23155         
23156         return this.groups[groupId] ;
23157     }
23158     
23159     
23160 });
23161 /*
23162  * - LGPL
23163  *
23164  * RadioItem
23165  * 
23166  */
23167
23168 /**
23169  * @class Roo.bootstrap.Radio
23170  * @extends Roo.bootstrap.Component
23171  * Bootstrap Radio class
23172  * @cfg {String} boxLabel - the label associated
23173  * @cfg {String} value - the value of radio
23174  * 
23175  * @constructor
23176  * Create a new Radio
23177  * @param {Object} config The config object
23178  */
23179 Roo.bootstrap.Radio = function(config){
23180     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23181     
23182 };
23183
23184 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23185     
23186     boxLabel : '',
23187     
23188     value : '',
23189     
23190     getAutoCreate : function()
23191     {
23192         var cfg = {
23193             tag : 'div',
23194             cls : 'form-group radio',
23195             cn : [
23196                 {
23197                     tag : 'label',
23198                     cls : 'box-label',
23199                     html : this.boxLabel
23200                 }
23201             ]
23202         };
23203         
23204         return cfg;
23205     },
23206     
23207     initEvents : function() 
23208     {
23209         this.parent().register(this);
23210         
23211         this.el.on('click', this.onClick, this);
23212         
23213     },
23214     
23215     onClick : function(e)
23216     {
23217         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23218             this.setChecked(true);
23219         }
23220     },
23221     
23222     setChecked : function(state, suppressEvent)
23223     {
23224         this.parent().setValue(this.value, suppressEvent);
23225         
23226     },
23227     
23228     setBoxLabel : function(v)
23229     {
23230         this.boxLabel = v;
23231         
23232         if(this.rendered){
23233             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23234         }
23235     }
23236     
23237 });
23238  
23239
23240  /*
23241  * - LGPL
23242  *
23243  * Input
23244  * 
23245  */
23246
23247 /**
23248  * @class Roo.bootstrap.SecurePass
23249  * @extends Roo.bootstrap.Input
23250  * Bootstrap SecurePass class
23251  *
23252  * 
23253  * @constructor
23254  * Create a new SecurePass
23255  * @param {Object} config The config object
23256  */
23257  
23258 Roo.bootstrap.SecurePass = function (config) {
23259     // these go here, so the translation tool can replace them..
23260     this.errors = {
23261         PwdEmpty: "Please type a password, and then retype it to confirm.",
23262         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23263         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23264         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23265         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23266         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23267         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23268         TooWeak: "Your password is Too Weak."
23269     },
23270     this.meterLabel = "Password strength:";
23271     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23272     this.meterClass = [
23273         "roo-password-meter-tooweak", 
23274         "roo-password-meter-weak", 
23275         "roo-password-meter-medium", 
23276         "roo-password-meter-strong", 
23277         "roo-password-meter-grey"
23278     ];
23279     
23280     this.errors = {};
23281     
23282     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23283 }
23284
23285 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23286     /**
23287      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23288      * {
23289      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23290      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23291      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23292      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23293      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23294      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23295      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23296      * })
23297      */
23298     // private
23299     
23300     meterWidth: 300,
23301     errorMsg :'',    
23302     errors: false,
23303     imageRoot: '/',
23304     /**
23305      * @cfg {String/Object} Label for the strength meter (defaults to
23306      * 'Password strength:')
23307      */
23308     // private
23309     meterLabel: '',
23310     /**
23311      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23312      * ['Weak', 'Medium', 'Strong'])
23313      */
23314     // private    
23315     pwdStrengths: false,    
23316     // private
23317     strength: 0,
23318     // private
23319     _lastPwd: null,
23320     // private
23321     kCapitalLetter: 0,
23322     kSmallLetter: 1,
23323     kDigit: 2,
23324     kPunctuation: 3,
23325     
23326     insecure: false,
23327     // private
23328     initEvents: function ()
23329     {
23330         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23331
23332         if (this.el.is('input[type=password]') && Roo.isSafari) {
23333             this.el.on('keydown', this.SafariOnKeyDown, this);
23334         }
23335
23336         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23337     },
23338     // private
23339     onRender: function (ct, position)
23340     {
23341         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23342         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23343         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23344
23345         this.trigger.createChild({
23346                    cn: [
23347                     {
23348                     //id: 'PwdMeter',
23349                     tag: 'div',
23350                     cls: 'roo-password-meter-grey col-xs-12',
23351                     style: {
23352                         //width: 0,
23353                         //width: this.meterWidth + 'px'                                                
23354                         }
23355                     },
23356                     {                            
23357                          cls: 'roo-password-meter-text'                          
23358                     }
23359                 ]            
23360         });
23361
23362          
23363         if (this.hideTrigger) {
23364             this.trigger.setDisplayed(false);
23365         }
23366         this.setSize(this.width || '', this.height || '');
23367     },
23368     // private
23369     onDestroy: function ()
23370     {
23371         if (this.trigger) {
23372             this.trigger.removeAllListeners();
23373             this.trigger.remove();
23374         }
23375         if (this.wrap) {
23376             this.wrap.remove();
23377         }
23378         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23379     },
23380     // private
23381     checkStrength: function ()
23382     {
23383         var pwd = this.inputEl().getValue();
23384         if (pwd == this._lastPwd) {
23385             return;
23386         }
23387
23388         var strength;
23389         if (this.ClientSideStrongPassword(pwd)) {
23390             strength = 3;
23391         } else if (this.ClientSideMediumPassword(pwd)) {
23392             strength = 2;
23393         } else if (this.ClientSideWeakPassword(pwd)) {
23394             strength = 1;
23395         } else {
23396             strength = 0;
23397         }
23398         
23399         Roo.log('strength1: ' + strength);
23400         
23401         //var pm = this.trigger.child('div/div/div').dom;
23402         var pm = this.trigger.child('div/div');
23403         pm.removeClass(this.meterClass);
23404         pm.addClass(this.meterClass[strength]);
23405                 
23406         
23407         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23408                 
23409         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23410         
23411         this._lastPwd = pwd;
23412     },
23413     reset: function ()
23414     {
23415         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23416         
23417         this._lastPwd = '';
23418         
23419         var pm = this.trigger.child('div/div');
23420         pm.removeClass(this.meterClass);
23421         pm.addClass('roo-password-meter-grey');        
23422         
23423         
23424         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23425         
23426         pt.innerHTML = '';
23427         this.inputEl().dom.type='password';
23428     },
23429     // private
23430     validateValue: function (value)
23431     {
23432         
23433         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23434             return false;
23435         }
23436         if (value.length == 0) {
23437             if (this.allowBlank) {
23438                 this.clearInvalid();
23439                 return true;
23440             }
23441
23442             this.markInvalid(this.errors.PwdEmpty);
23443             this.errorMsg = this.errors.PwdEmpty;
23444             return false;
23445         }
23446         
23447         if(this.insecure){
23448             return true;
23449         }
23450         
23451         if ('[\x21-\x7e]*'.match(value)) {
23452             this.markInvalid(this.errors.PwdBadChar);
23453             this.errorMsg = this.errors.PwdBadChar;
23454             return false;
23455         }
23456         if (value.length < 6) {
23457             this.markInvalid(this.errors.PwdShort);
23458             this.errorMsg = this.errors.PwdShort;
23459             return false;
23460         }
23461         if (value.length > 16) {
23462             this.markInvalid(this.errors.PwdLong);
23463             this.errorMsg = this.errors.PwdLong;
23464             return false;
23465         }
23466         var strength;
23467         if (this.ClientSideStrongPassword(value)) {
23468             strength = 3;
23469         } else if (this.ClientSideMediumPassword(value)) {
23470             strength = 2;
23471         } else if (this.ClientSideWeakPassword(value)) {
23472             strength = 1;
23473         } else {
23474             strength = 0;
23475         }
23476
23477         
23478         if (strength < 2) {
23479             //this.markInvalid(this.errors.TooWeak);
23480             this.errorMsg = this.errors.TooWeak;
23481             //return false;
23482         }
23483         
23484         
23485         console.log('strength2: ' + strength);
23486         
23487         //var pm = this.trigger.child('div/div/div').dom;
23488         
23489         var pm = this.trigger.child('div/div');
23490         pm.removeClass(this.meterClass);
23491         pm.addClass(this.meterClass[strength]);
23492                 
23493         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23494                 
23495         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23496         
23497         this.errorMsg = ''; 
23498         return true;
23499     },
23500     // private
23501     CharacterSetChecks: function (type)
23502     {
23503         this.type = type;
23504         this.fResult = false;
23505     },
23506     // private
23507     isctype: function (character, type)
23508     {
23509         switch (type) {  
23510             case this.kCapitalLetter:
23511                 if (character >= 'A' && character <= 'Z') {
23512                     return true;
23513                 }
23514                 break;
23515             
23516             case this.kSmallLetter:
23517                 if (character >= 'a' && character <= 'z') {
23518                     return true;
23519                 }
23520                 break;
23521             
23522             case this.kDigit:
23523                 if (character >= '0' && character <= '9') {
23524                     return true;
23525                 }
23526                 break;
23527             
23528             case this.kPunctuation:
23529                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23530                     return true;
23531                 }
23532                 break;
23533             
23534             default:
23535                 return false;
23536         }
23537
23538     },
23539     // private
23540     IsLongEnough: function (pwd, size)
23541     {
23542         return !(pwd == null || isNaN(size) || pwd.length < size);
23543     },
23544     // private
23545     SpansEnoughCharacterSets: function (word, nb)
23546     {
23547         if (!this.IsLongEnough(word, nb))
23548         {
23549             return false;
23550         }
23551
23552         var characterSetChecks = new Array(
23553             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23554             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23555         );
23556         
23557         for (var index = 0; index < word.length; ++index) {
23558             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23559                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23560                     characterSetChecks[nCharSet].fResult = true;
23561                     break;
23562                 }
23563             }
23564         }
23565
23566         var nCharSets = 0;
23567         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23568             if (characterSetChecks[nCharSet].fResult) {
23569                 ++nCharSets;
23570             }
23571         }
23572
23573         if (nCharSets < nb) {
23574             return false;
23575         }
23576         return true;
23577     },
23578     // private
23579     ClientSideStrongPassword: function (pwd)
23580     {
23581         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23582     },
23583     // private
23584     ClientSideMediumPassword: function (pwd)
23585     {
23586         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23587     },
23588     // private
23589     ClientSideWeakPassword: function (pwd)
23590     {
23591         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23592     }
23593           
23594 })//<script type="text/javascript">
23595
23596 /*
23597  * Based  Ext JS Library 1.1.1
23598  * Copyright(c) 2006-2007, Ext JS, LLC.
23599  * LGPL
23600  *
23601  */
23602  
23603 /**
23604  * @class Roo.HtmlEditorCore
23605  * @extends Roo.Component
23606  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23607  *
23608  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23609  */
23610
23611 Roo.HtmlEditorCore = function(config){
23612     
23613     
23614     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23615     
23616     
23617     this.addEvents({
23618         /**
23619          * @event initialize
23620          * Fires when the editor is fully initialized (including the iframe)
23621          * @param {Roo.HtmlEditorCore} this
23622          */
23623         initialize: true,
23624         /**
23625          * @event activate
23626          * Fires when the editor is first receives the focus. Any insertion must wait
23627          * until after this event.
23628          * @param {Roo.HtmlEditorCore} this
23629          */
23630         activate: true,
23631          /**
23632          * @event beforesync
23633          * Fires before the textarea is updated with content from the editor iframe. Return false
23634          * to cancel the sync.
23635          * @param {Roo.HtmlEditorCore} this
23636          * @param {String} html
23637          */
23638         beforesync: true,
23639          /**
23640          * @event beforepush
23641          * Fires before the iframe editor is updated with content from the textarea. Return false
23642          * to cancel the push.
23643          * @param {Roo.HtmlEditorCore} this
23644          * @param {String} html
23645          */
23646         beforepush: true,
23647          /**
23648          * @event sync
23649          * Fires when the textarea is updated with content from the editor iframe.
23650          * @param {Roo.HtmlEditorCore} this
23651          * @param {String} html
23652          */
23653         sync: true,
23654          /**
23655          * @event push
23656          * Fires when the iframe editor is updated with content from the textarea.
23657          * @param {Roo.HtmlEditorCore} this
23658          * @param {String} html
23659          */
23660         push: true,
23661         
23662         /**
23663          * @event editorevent
23664          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23665          * @param {Roo.HtmlEditorCore} this
23666          */
23667         editorevent: true
23668         
23669     });
23670     
23671     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23672     
23673     // defaults : white / black...
23674     this.applyBlacklists();
23675     
23676     
23677     
23678 };
23679
23680
23681 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23682
23683
23684      /**
23685      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23686      */
23687     
23688     owner : false,
23689     
23690      /**
23691      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23692      *                        Roo.resizable.
23693      */
23694     resizable : false,
23695      /**
23696      * @cfg {Number} height (in pixels)
23697      */   
23698     height: 300,
23699    /**
23700      * @cfg {Number} width (in pixels)
23701      */   
23702     width: 500,
23703     
23704     /**
23705      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23706      * 
23707      */
23708     stylesheets: false,
23709     
23710     // id of frame..
23711     frameId: false,
23712     
23713     // private properties
23714     validationEvent : false,
23715     deferHeight: true,
23716     initialized : false,
23717     activated : false,
23718     sourceEditMode : false,
23719     onFocus : Roo.emptyFn,
23720     iframePad:3,
23721     hideMode:'offsets',
23722     
23723     clearUp: true,
23724     
23725     // blacklist + whitelisted elements..
23726     black: false,
23727     white: false,
23728      
23729     bodyCls : '',
23730
23731     /**
23732      * Protected method that will not generally be called directly. It
23733      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23734      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23735      */
23736     getDocMarkup : function(){
23737         // body styles..
23738         var st = '';
23739         
23740         // inherit styels from page...?? 
23741         if (this.stylesheets === false) {
23742             
23743             Roo.get(document.head).select('style').each(function(node) {
23744                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23745             });
23746             
23747             Roo.get(document.head).select('link').each(function(node) { 
23748                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23749             });
23750             
23751         } else if (!this.stylesheets.length) {
23752                 // simple..
23753                 st = '<style type="text/css">' +
23754                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23755                    '</style>';
23756         } else { 
23757             st = '<style type="text/css">' +
23758                     this.stylesheets +
23759                 '</style>';
23760         }
23761         
23762         st +=  '<style type="text/css">' +
23763             'IMG { cursor: pointer } ' +
23764         '</style>';
23765
23766         var cls = 'roo-htmleditor-body';
23767         
23768         if(this.bodyCls.length){
23769             cls += ' ' + this.bodyCls;
23770         }
23771         
23772         return '<html><head>' + st  +
23773             //<style type="text/css">' +
23774             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23775             //'</style>' +
23776             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23777     },
23778
23779     // private
23780     onRender : function(ct, position)
23781     {
23782         var _t = this;
23783         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23784         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23785         
23786         
23787         this.el.dom.style.border = '0 none';
23788         this.el.dom.setAttribute('tabIndex', -1);
23789         this.el.addClass('x-hidden hide');
23790         
23791         
23792         
23793         if(Roo.isIE){ // fix IE 1px bogus margin
23794             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23795         }
23796        
23797         
23798         this.frameId = Roo.id();
23799         
23800          
23801         
23802         var iframe = this.owner.wrap.createChild({
23803             tag: 'iframe',
23804             cls: 'form-control', // bootstrap..
23805             id: this.frameId,
23806             name: this.frameId,
23807             frameBorder : 'no',
23808             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23809         }, this.el
23810         );
23811         
23812         
23813         this.iframe = iframe.dom;
23814
23815          this.assignDocWin();
23816         
23817         this.doc.designMode = 'on';
23818        
23819         this.doc.open();
23820         this.doc.write(this.getDocMarkup());
23821         this.doc.close();
23822
23823         
23824         var task = { // must defer to wait for browser to be ready
23825             run : function(){
23826                 //console.log("run task?" + this.doc.readyState);
23827                 this.assignDocWin();
23828                 if(this.doc.body || this.doc.readyState == 'complete'){
23829                     try {
23830                         this.doc.designMode="on";
23831                     } catch (e) {
23832                         return;
23833                     }
23834                     Roo.TaskMgr.stop(task);
23835                     this.initEditor.defer(10, this);
23836                 }
23837             },
23838             interval : 10,
23839             duration: 10000,
23840             scope: this
23841         };
23842         Roo.TaskMgr.start(task);
23843
23844     },
23845
23846     // private
23847     onResize : function(w, h)
23848     {
23849          Roo.log('resize: ' +w + ',' + h );
23850         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23851         if(!this.iframe){
23852             return;
23853         }
23854         if(typeof w == 'number'){
23855             
23856             this.iframe.style.width = w + 'px';
23857         }
23858         if(typeof h == 'number'){
23859             
23860             this.iframe.style.height = h + 'px';
23861             if(this.doc){
23862                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23863             }
23864         }
23865         
23866     },
23867
23868     /**
23869      * Toggles the editor between standard and source edit mode.
23870      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23871      */
23872     toggleSourceEdit : function(sourceEditMode){
23873         
23874         this.sourceEditMode = sourceEditMode === true;
23875         
23876         if(this.sourceEditMode){
23877  
23878             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23879             
23880         }else{
23881             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23882             //this.iframe.className = '';
23883             this.deferFocus();
23884         }
23885         //this.setSize(this.owner.wrap.getSize());
23886         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23887     },
23888
23889     
23890   
23891
23892     /**
23893      * Protected method that will not generally be called directly. If you need/want
23894      * custom HTML cleanup, this is the method you should override.
23895      * @param {String} html The HTML to be cleaned
23896      * return {String} The cleaned HTML
23897      */
23898     cleanHtml : function(html){
23899         html = String(html);
23900         if(html.length > 5){
23901             if(Roo.isSafari){ // strip safari nonsense
23902                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23903             }
23904         }
23905         if(html == '&nbsp;'){
23906             html = '';
23907         }
23908         return html;
23909     },
23910
23911     /**
23912      * HTML Editor -> Textarea
23913      * Protected method that will not generally be called directly. Syncs the contents
23914      * of the editor iframe with the textarea.
23915      */
23916     syncValue : function(){
23917         if(this.initialized){
23918             var bd = (this.doc.body || this.doc.documentElement);
23919             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23920             var html = bd.innerHTML;
23921             if(Roo.isSafari){
23922                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23923                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23924                 if(m && m[1]){
23925                     html = '<div style="'+m[0]+'">' + html + '</div>';
23926                 }
23927             }
23928             html = this.cleanHtml(html);
23929             // fix up the special chars.. normaly like back quotes in word...
23930             // however we do not want to do this with chinese..
23931             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23932                 
23933                 var cc = match.charCodeAt();
23934
23935                 // Get the character value, handling surrogate pairs
23936                 if (match.length == 2) {
23937                     // It's a surrogate pair, calculate the Unicode code point
23938                     var high = match.charCodeAt(0) - 0xD800;
23939                     var low  = match.charCodeAt(1) - 0xDC00;
23940                     cc = (high * 0x400) + low + 0x10000;
23941                 }  else if (
23942                     (cc >= 0x4E00 && cc < 0xA000 ) ||
23943                     (cc >= 0x3400 && cc < 0x4E00 ) ||
23944                     (cc >= 0xf900 && cc < 0xfb00 )
23945                 ) {
23946                         return match;
23947                 }  
23948          
23949                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
23950                 return "&#" + cc + ";";
23951                 
23952                 
23953             });
23954             
23955             
23956              
23957             if(this.owner.fireEvent('beforesync', this, html) !== false){
23958                 this.el.dom.value = html;
23959                 this.owner.fireEvent('sync', this, html);
23960             }
23961         }
23962     },
23963
23964     /**
23965      * Protected method that will not generally be called directly. Pushes the value of the textarea
23966      * into the iframe editor.
23967      */
23968     pushValue : function(){
23969         if(this.initialized){
23970             var v = this.el.dom.value.trim();
23971             
23972 //            if(v.length < 1){
23973 //                v = '&#160;';
23974 //            }
23975             
23976             if(this.owner.fireEvent('beforepush', this, v) !== false){
23977                 var d = (this.doc.body || this.doc.documentElement);
23978                 d.innerHTML = v;
23979                 this.cleanUpPaste();
23980                 this.el.dom.value = d.innerHTML;
23981                 this.owner.fireEvent('push', this, v);
23982             }
23983         }
23984     },
23985
23986     // private
23987     deferFocus : function(){
23988         this.focus.defer(10, this);
23989     },
23990
23991     // doc'ed in Field
23992     focus : function(){
23993         if(this.win && !this.sourceEditMode){
23994             this.win.focus();
23995         }else{
23996             this.el.focus();
23997         }
23998     },
23999     
24000     assignDocWin: function()
24001     {
24002         var iframe = this.iframe;
24003         
24004          if(Roo.isIE){
24005             this.doc = iframe.contentWindow.document;
24006             this.win = iframe.contentWindow;
24007         } else {
24008 //            if (!Roo.get(this.frameId)) {
24009 //                return;
24010 //            }
24011 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24012 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24013             
24014             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24015                 return;
24016             }
24017             
24018             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24019             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24020         }
24021     },
24022     
24023     // private
24024     initEditor : function(){
24025         //console.log("INIT EDITOR");
24026         this.assignDocWin();
24027         
24028         
24029         
24030         this.doc.designMode="on";
24031         this.doc.open();
24032         this.doc.write(this.getDocMarkup());
24033         this.doc.close();
24034         
24035         var dbody = (this.doc.body || this.doc.documentElement);
24036         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24037         // this copies styles from the containing element into thsi one..
24038         // not sure why we need all of this..
24039         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24040         
24041         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24042         //ss['background-attachment'] = 'fixed'; // w3c
24043         dbody.bgProperties = 'fixed'; // ie
24044         //Roo.DomHelper.applyStyles(dbody, ss);
24045         Roo.EventManager.on(this.doc, {
24046             //'mousedown': this.onEditorEvent,
24047             'mouseup': this.onEditorEvent,
24048             'dblclick': this.onEditorEvent,
24049             'click': this.onEditorEvent,
24050             'keyup': this.onEditorEvent,
24051             buffer:100,
24052             scope: this
24053         });
24054         if(Roo.isGecko){
24055             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24056         }
24057         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24058             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24059         }
24060         this.initialized = true;
24061
24062         this.owner.fireEvent('initialize', this);
24063         this.pushValue();
24064     },
24065
24066     // private
24067     onDestroy : function(){
24068         
24069         
24070         
24071         if(this.rendered){
24072             
24073             //for (var i =0; i < this.toolbars.length;i++) {
24074             //    // fixme - ask toolbars for heights?
24075             //    this.toolbars[i].onDestroy();
24076            // }
24077             
24078             //this.wrap.dom.innerHTML = '';
24079             //this.wrap.remove();
24080         }
24081     },
24082
24083     // private
24084     onFirstFocus : function(){
24085         
24086         this.assignDocWin();
24087         
24088         
24089         this.activated = true;
24090          
24091     
24092         if(Roo.isGecko){ // prevent silly gecko errors
24093             this.win.focus();
24094             var s = this.win.getSelection();
24095             if(!s.focusNode || s.focusNode.nodeType != 3){
24096                 var r = s.getRangeAt(0);
24097                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24098                 r.collapse(true);
24099                 this.deferFocus();
24100             }
24101             try{
24102                 this.execCmd('useCSS', true);
24103                 this.execCmd('styleWithCSS', false);
24104             }catch(e){}
24105         }
24106         this.owner.fireEvent('activate', this);
24107     },
24108
24109     // private
24110     adjustFont: function(btn){
24111         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24112         //if(Roo.isSafari){ // safari
24113         //    adjust *= 2;
24114        // }
24115         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24116         if(Roo.isSafari){ // safari
24117             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24118             v =  (v < 10) ? 10 : v;
24119             v =  (v > 48) ? 48 : v;
24120             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24121             
24122         }
24123         
24124         
24125         v = Math.max(1, v+adjust);
24126         
24127         this.execCmd('FontSize', v  );
24128     },
24129
24130     onEditorEvent : function(e)
24131     {
24132         this.owner.fireEvent('editorevent', this, e);
24133       //  this.updateToolbar();
24134         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24135     },
24136
24137     insertTag : function(tg)
24138     {
24139         // could be a bit smarter... -> wrap the current selected tRoo..
24140         if (tg.toLowerCase() == 'span' ||
24141             tg.toLowerCase() == 'code' ||
24142             tg.toLowerCase() == 'sup' ||
24143             tg.toLowerCase() == 'sub' 
24144             ) {
24145             
24146             range = this.createRange(this.getSelection());
24147             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24148             wrappingNode.appendChild(range.extractContents());
24149             range.insertNode(wrappingNode);
24150
24151             return;
24152             
24153             
24154             
24155         }
24156         this.execCmd("formatblock",   tg);
24157         
24158     },
24159     
24160     insertText : function(txt)
24161     {
24162         
24163         
24164         var range = this.createRange();
24165         range.deleteContents();
24166                //alert(Sender.getAttribute('label'));
24167                
24168         range.insertNode(this.doc.createTextNode(txt));
24169     } ,
24170     
24171      
24172
24173     /**
24174      * Executes a Midas editor command on the editor document and performs necessary focus and
24175      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24176      * @param {String} cmd The Midas command
24177      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24178      */
24179     relayCmd : function(cmd, value){
24180         this.win.focus();
24181         this.execCmd(cmd, value);
24182         this.owner.fireEvent('editorevent', this);
24183         //this.updateToolbar();
24184         this.owner.deferFocus();
24185     },
24186
24187     /**
24188      * Executes a Midas editor command directly on the editor document.
24189      * For visual commands, you should use {@link #relayCmd} instead.
24190      * <b>This should only be called after the editor is initialized.</b>
24191      * @param {String} cmd The Midas command
24192      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24193      */
24194     execCmd : function(cmd, value){
24195         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24196         this.syncValue();
24197     },
24198  
24199  
24200    
24201     /**
24202      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24203      * to insert tRoo.
24204      * @param {String} text | dom node.. 
24205      */
24206     insertAtCursor : function(text)
24207     {
24208         
24209         if(!this.activated){
24210             return;
24211         }
24212         /*
24213         if(Roo.isIE){
24214             this.win.focus();
24215             var r = this.doc.selection.createRange();
24216             if(r){
24217                 r.collapse(true);
24218                 r.pasteHTML(text);
24219                 this.syncValue();
24220                 this.deferFocus();
24221             
24222             }
24223             return;
24224         }
24225         */
24226         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24227             this.win.focus();
24228             
24229             
24230             // from jquery ui (MIT licenced)
24231             var range, node;
24232             var win = this.win;
24233             
24234             if (win.getSelection && win.getSelection().getRangeAt) {
24235                 range = win.getSelection().getRangeAt(0);
24236                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24237                 range.insertNode(node);
24238             } else if (win.document.selection && win.document.selection.createRange) {
24239                 // no firefox support
24240                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24241                 win.document.selection.createRange().pasteHTML(txt);
24242             } else {
24243                 // no firefox support
24244                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24245                 this.execCmd('InsertHTML', txt);
24246             } 
24247             
24248             this.syncValue();
24249             
24250             this.deferFocus();
24251         }
24252     },
24253  // private
24254     mozKeyPress : function(e){
24255         if(e.ctrlKey){
24256             var c = e.getCharCode(), cmd;
24257           
24258             if(c > 0){
24259                 c = String.fromCharCode(c).toLowerCase();
24260                 switch(c){
24261                     case 'b':
24262                         cmd = 'bold';
24263                         break;
24264                     case 'i':
24265                         cmd = 'italic';
24266                         break;
24267                     
24268                     case 'u':
24269                         cmd = 'underline';
24270                         break;
24271                     
24272                     case 'v':
24273                         this.cleanUpPaste.defer(100, this);
24274                         return;
24275                         
24276                 }
24277                 if(cmd){
24278                     this.win.focus();
24279                     this.execCmd(cmd);
24280                     this.deferFocus();
24281                     e.preventDefault();
24282                 }
24283                 
24284             }
24285         }
24286     },
24287
24288     // private
24289     fixKeys : function(){ // load time branching for fastest keydown performance
24290         if(Roo.isIE){
24291             return function(e){
24292                 var k = e.getKey(), r;
24293                 if(k == e.TAB){
24294                     e.stopEvent();
24295                     r = this.doc.selection.createRange();
24296                     if(r){
24297                         r.collapse(true);
24298                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24299                         this.deferFocus();
24300                     }
24301                     return;
24302                 }
24303                 
24304                 if(k == e.ENTER){
24305                     r = this.doc.selection.createRange();
24306                     if(r){
24307                         var target = r.parentElement();
24308                         if(!target || target.tagName.toLowerCase() != 'li'){
24309                             e.stopEvent();
24310                             r.pasteHTML('<br />');
24311                             r.collapse(false);
24312                             r.select();
24313                         }
24314                     }
24315                 }
24316                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24317                     this.cleanUpPaste.defer(100, this);
24318                     return;
24319                 }
24320                 
24321                 
24322             };
24323         }else if(Roo.isOpera){
24324             return function(e){
24325                 var k = e.getKey();
24326                 if(k == e.TAB){
24327                     e.stopEvent();
24328                     this.win.focus();
24329                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24330                     this.deferFocus();
24331                 }
24332                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24333                     this.cleanUpPaste.defer(100, this);
24334                     return;
24335                 }
24336                 
24337             };
24338         }else if(Roo.isSafari){
24339             return function(e){
24340                 var k = e.getKey();
24341                 
24342                 if(k == e.TAB){
24343                     e.stopEvent();
24344                     this.execCmd('InsertText','\t');
24345                     this.deferFocus();
24346                     return;
24347                 }
24348                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24349                     this.cleanUpPaste.defer(100, this);
24350                     return;
24351                 }
24352                 
24353              };
24354         }
24355     }(),
24356     
24357     getAllAncestors: function()
24358     {
24359         var p = this.getSelectedNode();
24360         var a = [];
24361         if (!p) {
24362             a.push(p); // push blank onto stack..
24363             p = this.getParentElement();
24364         }
24365         
24366         
24367         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24368             a.push(p);
24369             p = p.parentNode;
24370         }
24371         a.push(this.doc.body);
24372         return a;
24373     },
24374     lastSel : false,
24375     lastSelNode : false,
24376     
24377     
24378     getSelection : function() 
24379     {
24380         this.assignDocWin();
24381         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24382     },
24383     
24384     getSelectedNode: function() 
24385     {
24386         // this may only work on Gecko!!!
24387         
24388         // should we cache this!!!!
24389         
24390         
24391         
24392          
24393         var range = this.createRange(this.getSelection()).cloneRange();
24394         
24395         if (Roo.isIE) {
24396             var parent = range.parentElement();
24397             while (true) {
24398                 var testRange = range.duplicate();
24399                 testRange.moveToElementText(parent);
24400                 if (testRange.inRange(range)) {
24401                     break;
24402                 }
24403                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24404                     break;
24405                 }
24406                 parent = parent.parentElement;
24407             }
24408             return parent;
24409         }
24410         
24411         // is ancestor a text element.
24412         var ac =  range.commonAncestorContainer;
24413         if (ac.nodeType == 3) {
24414             ac = ac.parentNode;
24415         }
24416         
24417         var ar = ac.childNodes;
24418          
24419         var nodes = [];
24420         var other_nodes = [];
24421         var has_other_nodes = false;
24422         for (var i=0;i<ar.length;i++) {
24423             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24424                 continue;
24425             }
24426             // fullly contained node.
24427             
24428             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24429                 nodes.push(ar[i]);
24430                 continue;
24431             }
24432             
24433             // probably selected..
24434             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24435                 other_nodes.push(ar[i]);
24436                 continue;
24437             }
24438             // outer..
24439             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24440                 continue;
24441             }
24442             
24443             
24444             has_other_nodes = true;
24445         }
24446         if (!nodes.length && other_nodes.length) {
24447             nodes= other_nodes;
24448         }
24449         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24450             return false;
24451         }
24452         
24453         return nodes[0];
24454     },
24455     createRange: function(sel)
24456     {
24457         // this has strange effects when using with 
24458         // top toolbar - not sure if it's a great idea.
24459         //this.editor.contentWindow.focus();
24460         if (typeof sel != "undefined") {
24461             try {
24462                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24463             } catch(e) {
24464                 return this.doc.createRange();
24465             }
24466         } else {
24467             return this.doc.createRange();
24468         }
24469     },
24470     getParentElement: function()
24471     {
24472         
24473         this.assignDocWin();
24474         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24475         
24476         var range = this.createRange(sel);
24477          
24478         try {
24479             var p = range.commonAncestorContainer;
24480             while (p.nodeType == 3) { // text node
24481                 p = p.parentNode;
24482             }
24483             return p;
24484         } catch (e) {
24485             return null;
24486         }
24487     
24488     },
24489     /***
24490      *
24491      * Range intersection.. the hard stuff...
24492      *  '-1' = before
24493      *  '0' = hits..
24494      *  '1' = after.
24495      *         [ -- selected range --- ]
24496      *   [fail]                        [fail]
24497      *
24498      *    basically..
24499      *      if end is before start or  hits it. fail.
24500      *      if start is after end or hits it fail.
24501      *
24502      *   if either hits (but other is outside. - then it's not 
24503      *   
24504      *    
24505      **/
24506     
24507     
24508     // @see http://www.thismuchiknow.co.uk/?p=64.
24509     rangeIntersectsNode : function(range, node)
24510     {
24511         var nodeRange = node.ownerDocument.createRange();
24512         try {
24513             nodeRange.selectNode(node);
24514         } catch (e) {
24515             nodeRange.selectNodeContents(node);
24516         }
24517     
24518         var rangeStartRange = range.cloneRange();
24519         rangeStartRange.collapse(true);
24520     
24521         var rangeEndRange = range.cloneRange();
24522         rangeEndRange.collapse(false);
24523     
24524         var nodeStartRange = nodeRange.cloneRange();
24525         nodeStartRange.collapse(true);
24526     
24527         var nodeEndRange = nodeRange.cloneRange();
24528         nodeEndRange.collapse(false);
24529     
24530         return rangeStartRange.compareBoundaryPoints(
24531                  Range.START_TO_START, nodeEndRange) == -1 &&
24532                rangeEndRange.compareBoundaryPoints(
24533                  Range.START_TO_START, nodeStartRange) == 1;
24534         
24535          
24536     },
24537     rangeCompareNode : function(range, node)
24538     {
24539         var nodeRange = node.ownerDocument.createRange();
24540         try {
24541             nodeRange.selectNode(node);
24542         } catch (e) {
24543             nodeRange.selectNodeContents(node);
24544         }
24545         
24546         
24547         range.collapse(true);
24548     
24549         nodeRange.collapse(true);
24550      
24551         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24552         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24553          
24554         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24555         
24556         var nodeIsBefore   =  ss == 1;
24557         var nodeIsAfter    = ee == -1;
24558         
24559         if (nodeIsBefore && nodeIsAfter) {
24560             return 0; // outer
24561         }
24562         if (!nodeIsBefore && nodeIsAfter) {
24563             return 1; //right trailed.
24564         }
24565         
24566         if (nodeIsBefore && !nodeIsAfter) {
24567             return 2;  // left trailed.
24568         }
24569         // fully contined.
24570         return 3;
24571     },
24572
24573     // private? - in a new class?
24574     cleanUpPaste :  function()
24575     {
24576         // cleans up the whole document..
24577         Roo.log('cleanuppaste');
24578         
24579         this.cleanUpChildren(this.doc.body);
24580         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24581         if (clean != this.doc.body.innerHTML) {
24582             this.doc.body.innerHTML = clean;
24583         }
24584         
24585     },
24586     
24587     cleanWordChars : function(input) {// change the chars to hex code
24588         var he = Roo.HtmlEditorCore;
24589         
24590         var output = input;
24591         Roo.each(he.swapCodes, function(sw) { 
24592             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24593             
24594             output = output.replace(swapper, sw[1]);
24595         });
24596         
24597         return output;
24598     },
24599     
24600     
24601     cleanUpChildren : function (n)
24602     {
24603         if (!n.childNodes.length) {
24604             return;
24605         }
24606         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24607            this.cleanUpChild(n.childNodes[i]);
24608         }
24609     },
24610     
24611     
24612         
24613     
24614     cleanUpChild : function (node)
24615     {
24616         var ed = this;
24617         //console.log(node);
24618         if (node.nodeName == "#text") {
24619             // clean up silly Windows -- stuff?
24620             return; 
24621         }
24622         if (node.nodeName == "#comment") {
24623             node.parentNode.removeChild(node);
24624             // clean up silly Windows -- stuff?
24625             return; 
24626         }
24627         var lcname = node.tagName.toLowerCase();
24628         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24629         // whitelist of tags..
24630         
24631         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24632             // remove node.
24633             node.parentNode.removeChild(node);
24634             return;
24635             
24636         }
24637         
24638         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24639         
24640         // spans with no attributes - just remove them..
24641         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24642             remove_keep_children = true;
24643         }
24644         
24645         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24646         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24647         
24648         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24649         //    remove_keep_children = true;
24650         //}
24651         
24652         if (remove_keep_children) {
24653             this.cleanUpChildren(node);
24654             // inserts everything just before this node...
24655             while (node.childNodes.length) {
24656                 var cn = node.childNodes[0];
24657                 node.removeChild(cn);
24658                 node.parentNode.insertBefore(cn, node);
24659             }
24660             node.parentNode.removeChild(node);
24661             return;
24662         }
24663         
24664         if (!node.attributes || !node.attributes.length) {
24665             
24666           
24667             
24668             
24669             this.cleanUpChildren(node);
24670             return;
24671         }
24672         
24673         function cleanAttr(n,v)
24674         {
24675             
24676             if (v.match(/^\./) || v.match(/^\//)) {
24677                 return;
24678             }
24679             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24680                 return;
24681             }
24682             if (v.match(/^#/)) {
24683                 return;
24684             }
24685 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24686             node.removeAttribute(n);
24687             
24688         }
24689         
24690         var cwhite = this.cwhite;
24691         var cblack = this.cblack;
24692             
24693         function cleanStyle(n,v)
24694         {
24695             if (v.match(/expression/)) { //XSS?? should we even bother..
24696                 node.removeAttribute(n);
24697                 return;
24698             }
24699             
24700             var parts = v.split(/;/);
24701             var clean = [];
24702             
24703             Roo.each(parts, function(p) {
24704                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24705                 if (!p.length) {
24706                     return true;
24707                 }
24708                 var l = p.split(':').shift().replace(/\s+/g,'');
24709                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24710                 
24711                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24712 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24713                     //node.removeAttribute(n);
24714                     return true;
24715                 }
24716                 //Roo.log()
24717                 // only allow 'c whitelisted system attributes'
24718                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24719 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24720                     //node.removeAttribute(n);
24721                     return true;
24722                 }
24723                 
24724                 
24725                  
24726                 
24727                 clean.push(p);
24728                 return true;
24729             });
24730             if (clean.length) { 
24731                 node.setAttribute(n, clean.join(';'));
24732             } else {
24733                 node.removeAttribute(n);
24734             }
24735             
24736         }
24737         
24738         
24739         for (var i = node.attributes.length-1; i > -1 ; i--) {
24740             var a = node.attributes[i];
24741             //console.log(a);
24742             
24743             if (a.name.toLowerCase().substr(0,2)=='on')  {
24744                 node.removeAttribute(a.name);
24745                 continue;
24746             }
24747             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24748                 node.removeAttribute(a.name);
24749                 continue;
24750             }
24751             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24752                 cleanAttr(a.name,a.value); // fixme..
24753                 continue;
24754             }
24755             if (a.name == 'style') {
24756                 cleanStyle(a.name,a.value);
24757                 continue;
24758             }
24759             /// clean up MS crap..
24760             // tecnically this should be a list of valid class'es..
24761             
24762             
24763             if (a.name == 'class') {
24764                 if (a.value.match(/^Mso/)) {
24765                     node.removeAttribute('class');
24766                 }
24767                 
24768                 if (a.value.match(/^body$/)) {
24769                     node.removeAttribute('class');
24770                 }
24771                 continue;
24772             }
24773             
24774             // style cleanup!?
24775             // class cleanup?
24776             
24777         }
24778         
24779         
24780         this.cleanUpChildren(node);
24781         
24782         
24783     },
24784     
24785     /**
24786      * Clean up MS wordisms...
24787      */
24788     cleanWord : function(node)
24789     {
24790         if (!node) {
24791             this.cleanWord(this.doc.body);
24792             return;
24793         }
24794         
24795         if(
24796                 node.nodeName == 'SPAN' &&
24797                 !node.hasAttributes() &&
24798                 node.childNodes.length == 1 &&
24799                 node.firstChild.nodeName == "#text"  
24800         ) {
24801             var textNode = node.firstChild;
24802             node.removeChild(textNode);
24803             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24804                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24805             }
24806             node.parentNode.insertBefore(textNode, node);
24807             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24808                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24809             }
24810             node.parentNode.removeChild(node);
24811         }
24812         
24813         if (node.nodeName == "#text") {
24814             // clean up silly Windows -- stuff?
24815             return; 
24816         }
24817         if (node.nodeName == "#comment") {
24818             node.parentNode.removeChild(node);
24819             // clean up silly Windows -- stuff?
24820             return; 
24821         }
24822         
24823         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24824             node.parentNode.removeChild(node);
24825             return;
24826         }
24827         //Roo.log(node.tagName);
24828         // remove - but keep children..
24829         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24830             //Roo.log('-- removed');
24831             while (node.childNodes.length) {
24832                 var cn = node.childNodes[0];
24833                 node.removeChild(cn);
24834                 node.parentNode.insertBefore(cn, node);
24835                 // move node to parent - and clean it..
24836                 this.cleanWord(cn);
24837             }
24838             node.parentNode.removeChild(node);
24839             /// no need to iterate chidlren = it's got none..
24840             //this.iterateChildren(node, this.cleanWord);
24841             return;
24842         }
24843         // clean styles
24844         if (node.className.length) {
24845             
24846             var cn = node.className.split(/\W+/);
24847             var cna = [];
24848             Roo.each(cn, function(cls) {
24849                 if (cls.match(/Mso[a-zA-Z]+/)) {
24850                     return;
24851                 }
24852                 cna.push(cls);
24853             });
24854             node.className = cna.length ? cna.join(' ') : '';
24855             if (!cna.length) {
24856                 node.removeAttribute("class");
24857             }
24858         }
24859         
24860         if (node.hasAttribute("lang")) {
24861             node.removeAttribute("lang");
24862         }
24863         
24864         if (node.hasAttribute("style")) {
24865             
24866             var styles = node.getAttribute("style").split(";");
24867             var nstyle = [];
24868             Roo.each(styles, function(s) {
24869                 if (!s.match(/:/)) {
24870                     return;
24871                 }
24872                 var kv = s.split(":");
24873                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24874                     return;
24875                 }
24876                 // what ever is left... we allow.
24877                 nstyle.push(s);
24878             });
24879             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24880             if (!nstyle.length) {
24881                 node.removeAttribute('style');
24882             }
24883         }
24884         this.iterateChildren(node, this.cleanWord);
24885         
24886         
24887         
24888     },
24889     /**
24890      * iterateChildren of a Node, calling fn each time, using this as the scole..
24891      * @param {DomNode} node node to iterate children of.
24892      * @param {Function} fn method of this class to call on each item.
24893      */
24894     iterateChildren : function(node, fn)
24895     {
24896         if (!node.childNodes.length) {
24897                 return;
24898         }
24899         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24900            fn.call(this, node.childNodes[i])
24901         }
24902     },
24903     
24904     
24905     /**
24906      * cleanTableWidths.
24907      *
24908      * Quite often pasting from word etc.. results in tables with column and widths.
24909      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24910      *
24911      */
24912     cleanTableWidths : function(node)
24913     {
24914          
24915          
24916         if (!node) {
24917             this.cleanTableWidths(this.doc.body);
24918             return;
24919         }
24920         
24921         // ignore list...
24922         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24923             return; 
24924         }
24925         Roo.log(node.tagName);
24926         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24927             this.iterateChildren(node, this.cleanTableWidths);
24928             return;
24929         }
24930         if (node.hasAttribute('width')) {
24931             node.removeAttribute('width');
24932         }
24933         
24934          
24935         if (node.hasAttribute("style")) {
24936             // pretty basic...
24937             
24938             var styles = node.getAttribute("style").split(";");
24939             var nstyle = [];
24940             Roo.each(styles, function(s) {
24941                 if (!s.match(/:/)) {
24942                     return;
24943                 }
24944                 var kv = s.split(":");
24945                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
24946                     return;
24947                 }
24948                 // what ever is left... we allow.
24949                 nstyle.push(s);
24950             });
24951             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24952             if (!nstyle.length) {
24953                 node.removeAttribute('style');
24954             }
24955         }
24956         
24957         this.iterateChildren(node, this.cleanTableWidths);
24958         
24959         
24960     },
24961     
24962     
24963     
24964     
24965     domToHTML : function(currentElement, depth, nopadtext) {
24966         
24967         depth = depth || 0;
24968         nopadtext = nopadtext || false;
24969     
24970         if (!currentElement) {
24971             return this.domToHTML(this.doc.body);
24972         }
24973         
24974         //Roo.log(currentElement);
24975         var j;
24976         var allText = false;
24977         var nodeName = currentElement.nodeName;
24978         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
24979         
24980         if  (nodeName == '#text') {
24981             
24982             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
24983         }
24984         
24985         
24986         var ret = '';
24987         if (nodeName != 'BODY') {
24988              
24989             var i = 0;
24990             // Prints the node tagName, such as <A>, <IMG>, etc
24991             if (tagName) {
24992                 var attr = [];
24993                 for(i = 0; i < currentElement.attributes.length;i++) {
24994                     // quoting?
24995                     var aname = currentElement.attributes.item(i).name;
24996                     if (!currentElement.attributes.item(i).value.length) {
24997                         continue;
24998                     }
24999                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25000                 }
25001                 
25002                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25003             } 
25004             else {
25005                 
25006                 // eack
25007             }
25008         } else {
25009             tagName = false;
25010         }
25011         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25012             return ret;
25013         }
25014         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25015             nopadtext = true;
25016         }
25017         
25018         
25019         // Traverse the tree
25020         i = 0;
25021         var currentElementChild = currentElement.childNodes.item(i);
25022         var allText = true;
25023         var innerHTML  = '';
25024         lastnode = '';
25025         while (currentElementChild) {
25026             // Formatting code (indent the tree so it looks nice on the screen)
25027             var nopad = nopadtext;
25028             if (lastnode == 'SPAN') {
25029                 nopad  = true;
25030             }
25031             // text
25032             if  (currentElementChild.nodeName == '#text') {
25033                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25034                 toadd = nopadtext ? toadd : toadd.trim();
25035                 if (!nopad && toadd.length > 80) {
25036                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25037                 }
25038                 innerHTML  += toadd;
25039                 
25040                 i++;
25041                 currentElementChild = currentElement.childNodes.item(i);
25042                 lastNode = '';
25043                 continue;
25044             }
25045             allText = false;
25046             
25047             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25048                 
25049             // Recursively traverse the tree structure of the child node
25050             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25051             lastnode = currentElementChild.nodeName;
25052             i++;
25053             currentElementChild=currentElement.childNodes.item(i);
25054         }
25055         
25056         ret += innerHTML;
25057         
25058         if (!allText) {
25059                 // The remaining code is mostly for formatting the tree
25060             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25061         }
25062         
25063         
25064         if (tagName) {
25065             ret+= "</"+tagName+">";
25066         }
25067         return ret;
25068         
25069     },
25070         
25071     applyBlacklists : function()
25072     {
25073         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25074         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25075         
25076         this.white = [];
25077         this.black = [];
25078         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25079             if (b.indexOf(tag) > -1) {
25080                 return;
25081             }
25082             this.white.push(tag);
25083             
25084         }, this);
25085         
25086         Roo.each(w, function(tag) {
25087             if (b.indexOf(tag) > -1) {
25088                 return;
25089             }
25090             if (this.white.indexOf(tag) > -1) {
25091                 return;
25092             }
25093             this.white.push(tag);
25094             
25095         }, this);
25096         
25097         
25098         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25099             if (w.indexOf(tag) > -1) {
25100                 return;
25101             }
25102             this.black.push(tag);
25103             
25104         }, this);
25105         
25106         Roo.each(b, function(tag) {
25107             if (w.indexOf(tag) > -1) {
25108                 return;
25109             }
25110             if (this.black.indexOf(tag) > -1) {
25111                 return;
25112             }
25113             this.black.push(tag);
25114             
25115         }, this);
25116         
25117         
25118         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25119         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25120         
25121         this.cwhite = [];
25122         this.cblack = [];
25123         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25124             if (b.indexOf(tag) > -1) {
25125                 return;
25126             }
25127             this.cwhite.push(tag);
25128             
25129         }, this);
25130         
25131         Roo.each(w, function(tag) {
25132             if (b.indexOf(tag) > -1) {
25133                 return;
25134             }
25135             if (this.cwhite.indexOf(tag) > -1) {
25136                 return;
25137             }
25138             this.cwhite.push(tag);
25139             
25140         }, this);
25141         
25142         
25143         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25144             if (w.indexOf(tag) > -1) {
25145                 return;
25146             }
25147             this.cblack.push(tag);
25148             
25149         }, this);
25150         
25151         Roo.each(b, function(tag) {
25152             if (w.indexOf(tag) > -1) {
25153                 return;
25154             }
25155             if (this.cblack.indexOf(tag) > -1) {
25156                 return;
25157             }
25158             this.cblack.push(tag);
25159             
25160         }, this);
25161     },
25162     
25163     setStylesheets : function(stylesheets)
25164     {
25165         if(typeof(stylesheets) == 'string'){
25166             Roo.get(this.iframe.contentDocument.head).createChild({
25167                 tag : 'link',
25168                 rel : 'stylesheet',
25169                 type : 'text/css',
25170                 href : stylesheets
25171             });
25172             
25173             return;
25174         }
25175         var _this = this;
25176      
25177         Roo.each(stylesheets, function(s) {
25178             if(!s.length){
25179                 return;
25180             }
25181             
25182             Roo.get(_this.iframe.contentDocument.head).createChild({
25183                 tag : 'link',
25184                 rel : 'stylesheet',
25185                 type : 'text/css',
25186                 href : s
25187             });
25188         });
25189
25190         
25191     },
25192     
25193     removeStylesheets : function()
25194     {
25195         var _this = this;
25196         
25197         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25198             s.remove();
25199         });
25200     },
25201     
25202     setStyle : function(style)
25203     {
25204         Roo.get(this.iframe.contentDocument.head).createChild({
25205             tag : 'style',
25206             type : 'text/css',
25207             html : style
25208         });
25209
25210         return;
25211     }
25212     
25213     // hide stuff that is not compatible
25214     /**
25215      * @event blur
25216      * @hide
25217      */
25218     /**
25219      * @event change
25220      * @hide
25221      */
25222     /**
25223      * @event focus
25224      * @hide
25225      */
25226     /**
25227      * @event specialkey
25228      * @hide
25229      */
25230     /**
25231      * @cfg {String} fieldClass @hide
25232      */
25233     /**
25234      * @cfg {String} focusClass @hide
25235      */
25236     /**
25237      * @cfg {String} autoCreate @hide
25238      */
25239     /**
25240      * @cfg {String} inputType @hide
25241      */
25242     /**
25243      * @cfg {String} invalidClass @hide
25244      */
25245     /**
25246      * @cfg {String} invalidText @hide
25247      */
25248     /**
25249      * @cfg {String} msgFx @hide
25250      */
25251     /**
25252      * @cfg {String} validateOnBlur @hide
25253      */
25254 });
25255
25256 Roo.HtmlEditorCore.white = [
25257         'area', 'br', 'img', 'input', 'hr', 'wbr',
25258         
25259        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25260        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25261        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25262        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25263        'table',   'ul',         'xmp', 
25264        
25265        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25266       'thead',   'tr', 
25267      
25268       'dir', 'menu', 'ol', 'ul', 'dl',
25269        
25270       'embed',  'object'
25271 ];
25272
25273
25274 Roo.HtmlEditorCore.black = [
25275     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25276         'applet', // 
25277         'base',   'basefont', 'bgsound', 'blink',  'body', 
25278         'frame',  'frameset', 'head',    'html',   'ilayer', 
25279         'iframe', 'layer',  'link',     'meta',    'object',   
25280         'script', 'style' ,'title',  'xml' // clean later..
25281 ];
25282 Roo.HtmlEditorCore.clean = [
25283     'script', 'style', 'title', 'xml'
25284 ];
25285 Roo.HtmlEditorCore.remove = [
25286     'font'
25287 ];
25288 // attributes..
25289
25290 Roo.HtmlEditorCore.ablack = [
25291     'on'
25292 ];
25293     
25294 Roo.HtmlEditorCore.aclean = [ 
25295     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25296 ];
25297
25298 // protocols..
25299 Roo.HtmlEditorCore.pwhite= [
25300         'http',  'https',  'mailto'
25301 ];
25302
25303 // white listed style attributes.
25304 Roo.HtmlEditorCore.cwhite= [
25305       //  'text-align', /// default is to allow most things..
25306       
25307          
25308 //        'font-size'//??
25309 ];
25310
25311 // black listed style attributes.
25312 Roo.HtmlEditorCore.cblack= [
25313       //  'font-size' -- this can be set by the project 
25314 ];
25315
25316
25317 Roo.HtmlEditorCore.swapCodes   =[ 
25318     [    8211, "--" ], 
25319     [    8212, "--" ], 
25320     [    8216,  "'" ],  
25321     [    8217, "'" ],  
25322     [    8220, '"' ],  
25323     [    8221, '"' ],  
25324     [    8226, "*" ],  
25325     [    8230, "..." ]
25326 ]; 
25327
25328     /*
25329  * - LGPL
25330  *
25331  * HtmlEditor
25332  * 
25333  */
25334
25335 /**
25336  * @class Roo.bootstrap.HtmlEditor
25337  * @extends Roo.bootstrap.TextArea
25338  * Bootstrap HtmlEditor class
25339
25340  * @constructor
25341  * Create a new HtmlEditor
25342  * @param {Object} config The config object
25343  */
25344
25345 Roo.bootstrap.HtmlEditor = function(config){
25346     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25347     if (!this.toolbars) {
25348         this.toolbars = [];
25349     }
25350     
25351     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25352     this.addEvents({
25353             /**
25354              * @event initialize
25355              * Fires when the editor is fully initialized (including the iframe)
25356              * @param {HtmlEditor} this
25357              */
25358             initialize: true,
25359             /**
25360              * @event activate
25361              * Fires when the editor is first receives the focus. Any insertion must wait
25362              * until after this event.
25363              * @param {HtmlEditor} this
25364              */
25365             activate: true,
25366              /**
25367              * @event beforesync
25368              * Fires before the textarea is updated with content from the editor iframe. Return false
25369              * to cancel the sync.
25370              * @param {HtmlEditor} this
25371              * @param {String} html
25372              */
25373             beforesync: true,
25374              /**
25375              * @event beforepush
25376              * Fires before the iframe editor is updated with content from the textarea. Return false
25377              * to cancel the push.
25378              * @param {HtmlEditor} this
25379              * @param {String} html
25380              */
25381             beforepush: true,
25382              /**
25383              * @event sync
25384              * Fires when the textarea is updated with content from the editor iframe.
25385              * @param {HtmlEditor} this
25386              * @param {String} html
25387              */
25388             sync: true,
25389              /**
25390              * @event push
25391              * Fires when the iframe editor is updated with content from the textarea.
25392              * @param {HtmlEditor} this
25393              * @param {String} html
25394              */
25395             push: true,
25396              /**
25397              * @event editmodechange
25398              * Fires when the editor switches edit modes
25399              * @param {HtmlEditor} this
25400              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25401              */
25402             editmodechange: true,
25403             /**
25404              * @event editorevent
25405              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25406              * @param {HtmlEditor} this
25407              */
25408             editorevent: true,
25409             /**
25410              * @event firstfocus
25411              * Fires when on first focus - needed by toolbars..
25412              * @param {HtmlEditor} this
25413              */
25414             firstfocus: true,
25415             /**
25416              * @event autosave
25417              * Auto save the htmlEditor value as a file into Events
25418              * @param {HtmlEditor} this
25419              */
25420             autosave: true,
25421             /**
25422              * @event savedpreview
25423              * preview the saved version of htmlEditor
25424              * @param {HtmlEditor} this
25425              */
25426             savedpreview: true
25427         });
25428 };
25429
25430
25431 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25432     
25433     
25434       /**
25435      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25436      */
25437     toolbars : false,
25438     
25439      /**
25440     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25441     */
25442     btns : [],
25443    
25444      /**
25445      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25446      *                        Roo.resizable.
25447      */
25448     resizable : false,
25449      /**
25450      * @cfg {Number} height (in pixels)
25451      */   
25452     height: 300,
25453    /**
25454      * @cfg {Number} width (in pixels)
25455      */   
25456     width: false,
25457     
25458     /**
25459      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25460      * 
25461      */
25462     stylesheets: false,
25463     
25464     // id of frame..
25465     frameId: false,
25466     
25467     // private properties
25468     validationEvent : false,
25469     deferHeight: true,
25470     initialized : false,
25471     activated : false,
25472     
25473     onFocus : Roo.emptyFn,
25474     iframePad:3,
25475     hideMode:'offsets',
25476     
25477     tbContainer : false,
25478     
25479     bodyCls : '',
25480     
25481     toolbarContainer :function() {
25482         return this.wrap.select('.x-html-editor-tb',true).first();
25483     },
25484
25485     /**
25486      * Protected method that will not generally be called directly. It
25487      * is called when the editor creates its toolbar. Override this method if you need to
25488      * add custom toolbar buttons.
25489      * @param {HtmlEditor} editor
25490      */
25491     createToolbar : function(){
25492         Roo.log('renewing');
25493         Roo.log("create toolbars");
25494         
25495         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25496         this.toolbars[0].render(this.toolbarContainer());
25497         
25498         return;
25499         
25500 //        if (!editor.toolbars || !editor.toolbars.length) {
25501 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25502 //        }
25503 //        
25504 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25505 //            editor.toolbars[i] = Roo.factory(
25506 //                    typeof(editor.toolbars[i]) == 'string' ?
25507 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25508 //                Roo.bootstrap.HtmlEditor);
25509 //            editor.toolbars[i].init(editor);
25510 //        }
25511     },
25512
25513      
25514     // private
25515     onRender : function(ct, position)
25516     {
25517        // Roo.log("Call onRender: " + this.xtype);
25518         var _t = this;
25519         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25520       
25521         this.wrap = this.inputEl().wrap({
25522             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25523         });
25524         
25525         this.editorcore.onRender(ct, position);
25526          
25527         if (this.resizable) {
25528             this.resizeEl = new Roo.Resizable(this.wrap, {
25529                 pinned : true,
25530                 wrap: true,
25531                 dynamic : true,
25532                 minHeight : this.height,
25533                 height: this.height,
25534                 handles : this.resizable,
25535                 width: this.width,
25536                 listeners : {
25537                     resize : function(r, w, h) {
25538                         _t.onResize(w,h); // -something
25539                     }
25540                 }
25541             });
25542             
25543         }
25544         this.createToolbar(this);
25545        
25546         
25547         if(!this.width && this.resizable){
25548             this.setSize(this.wrap.getSize());
25549         }
25550         if (this.resizeEl) {
25551             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25552             // should trigger onReize..
25553         }
25554         
25555     },
25556
25557     // private
25558     onResize : function(w, h)
25559     {
25560         Roo.log('resize: ' +w + ',' + h );
25561         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25562         var ew = false;
25563         var eh = false;
25564         
25565         if(this.inputEl() ){
25566             if(typeof w == 'number'){
25567                 var aw = w - this.wrap.getFrameWidth('lr');
25568                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25569                 ew = aw;
25570             }
25571             if(typeof h == 'number'){
25572                  var tbh = -11;  // fixme it needs to tool bar size!
25573                 for (var i =0; i < this.toolbars.length;i++) {
25574                     // fixme - ask toolbars for heights?
25575                     tbh += this.toolbars[i].el.getHeight();
25576                     //if (this.toolbars[i].footer) {
25577                     //    tbh += this.toolbars[i].footer.el.getHeight();
25578                     //}
25579                 }
25580               
25581                 
25582                 
25583                 
25584                 
25585                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25586                 ah -= 5; // knock a few pixes off for look..
25587                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25588                 var eh = ah;
25589             }
25590         }
25591         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25592         this.editorcore.onResize(ew,eh);
25593         
25594     },
25595
25596     /**
25597      * Toggles the editor between standard and source edit mode.
25598      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25599      */
25600     toggleSourceEdit : function(sourceEditMode)
25601     {
25602         this.editorcore.toggleSourceEdit(sourceEditMode);
25603         
25604         if(this.editorcore.sourceEditMode){
25605             Roo.log('editor - showing textarea');
25606             
25607 //            Roo.log('in');
25608 //            Roo.log(this.syncValue());
25609             this.syncValue();
25610             this.inputEl().removeClass(['hide', 'x-hidden']);
25611             this.inputEl().dom.removeAttribute('tabIndex');
25612             this.inputEl().focus();
25613         }else{
25614             Roo.log('editor - hiding textarea');
25615 //            Roo.log('out')
25616 //            Roo.log(this.pushValue()); 
25617             this.pushValue();
25618             
25619             this.inputEl().addClass(['hide', 'x-hidden']);
25620             this.inputEl().dom.setAttribute('tabIndex', -1);
25621             //this.deferFocus();
25622         }
25623          
25624         if(this.resizable){
25625             this.setSize(this.wrap.getSize());
25626         }
25627         
25628         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25629     },
25630  
25631     // private (for BoxComponent)
25632     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25633
25634     // private (for BoxComponent)
25635     getResizeEl : function(){
25636         return this.wrap;
25637     },
25638
25639     // private (for BoxComponent)
25640     getPositionEl : function(){
25641         return this.wrap;
25642     },
25643
25644     // private
25645     initEvents : function(){
25646         this.originalValue = this.getValue();
25647     },
25648
25649 //    /**
25650 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25651 //     * @method
25652 //     */
25653 //    markInvalid : Roo.emptyFn,
25654 //    /**
25655 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25656 //     * @method
25657 //     */
25658 //    clearInvalid : Roo.emptyFn,
25659
25660     setValue : function(v){
25661         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25662         this.editorcore.pushValue();
25663     },
25664
25665      
25666     // private
25667     deferFocus : function(){
25668         this.focus.defer(10, this);
25669     },
25670
25671     // doc'ed in Field
25672     focus : function(){
25673         this.editorcore.focus();
25674         
25675     },
25676       
25677
25678     // private
25679     onDestroy : function(){
25680         
25681         
25682         
25683         if(this.rendered){
25684             
25685             for (var i =0; i < this.toolbars.length;i++) {
25686                 // fixme - ask toolbars for heights?
25687                 this.toolbars[i].onDestroy();
25688             }
25689             
25690             this.wrap.dom.innerHTML = '';
25691             this.wrap.remove();
25692         }
25693     },
25694
25695     // private
25696     onFirstFocus : function(){
25697         //Roo.log("onFirstFocus");
25698         this.editorcore.onFirstFocus();
25699          for (var i =0; i < this.toolbars.length;i++) {
25700             this.toolbars[i].onFirstFocus();
25701         }
25702         
25703     },
25704     
25705     // private
25706     syncValue : function()
25707     {   
25708         this.editorcore.syncValue();
25709     },
25710     
25711     pushValue : function()
25712     {   
25713         this.editorcore.pushValue();
25714     }
25715      
25716     
25717     // hide stuff that is not compatible
25718     /**
25719      * @event blur
25720      * @hide
25721      */
25722     /**
25723      * @event change
25724      * @hide
25725      */
25726     /**
25727      * @event focus
25728      * @hide
25729      */
25730     /**
25731      * @event specialkey
25732      * @hide
25733      */
25734     /**
25735      * @cfg {String} fieldClass @hide
25736      */
25737     /**
25738      * @cfg {String} focusClass @hide
25739      */
25740     /**
25741      * @cfg {String} autoCreate @hide
25742      */
25743     /**
25744      * @cfg {String} inputType @hide
25745      */
25746      
25747     /**
25748      * @cfg {String} invalidText @hide
25749      */
25750     /**
25751      * @cfg {String} msgFx @hide
25752      */
25753     /**
25754      * @cfg {String} validateOnBlur @hide
25755      */
25756 });
25757  
25758     
25759    
25760    
25761    
25762       
25763 Roo.namespace('Roo.bootstrap.htmleditor');
25764 /**
25765  * @class Roo.bootstrap.HtmlEditorToolbar1
25766  * Basic Toolbar
25767  * 
25768  * @example
25769  * Usage:
25770  *
25771  new Roo.bootstrap.HtmlEditor({
25772     ....
25773     toolbars : [
25774         new Roo.bootstrap.HtmlEditorToolbar1({
25775             disable : { fonts: 1 , format: 1, ..., ... , ...],
25776             btns : [ .... ]
25777         })
25778     }
25779      
25780  * 
25781  * @cfg {Object} disable List of elements to disable..
25782  * @cfg {Array} btns List of additional buttons.
25783  * 
25784  * 
25785  * NEEDS Extra CSS? 
25786  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25787  */
25788  
25789 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25790 {
25791     
25792     Roo.apply(this, config);
25793     
25794     // default disabled, based on 'good practice'..
25795     this.disable = this.disable || {};
25796     Roo.applyIf(this.disable, {
25797         fontSize : true,
25798         colors : true,
25799         specialElements : true
25800     });
25801     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25802     
25803     this.editor = config.editor;
25804     this.editorcore = config.editor.editorcore;
25805     
25806     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25807     
25808     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25809     // dont call parent... till later.
25810 }
25811 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25812      
25813     bar : true,
25814     
25815     editor : false,
25816     editorcore : false,
25817     
25818     
25819     formats : [
25820         "p" ,  
25821         "h1","h2","h3","h4","h5","h6", 
25822         "pre", "code", 
25823         "abbr", "acronym", "address", "cite", "samp", "var",
25824         'div','span'
25825     ],
25826     
25827     onRender : function(ct, position)
25828     {
25829        // Roo.log("Call onRender: " + this.xtype);
25830         
25831        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25832        Roo.log(this.el);
25833        this.el.dom.style.marginBottom = '0';
25834        var _this = this;
25835        var editorcore = this.editorcore;
25836        var editor= this.editor;
25837        
25838        var children = [];
25839        var btn = function(id,cmd , toggle, handler, html){
25840        
25841             var  event = toggle ? 'toggle' : 'click';
25842        
25843             var a = {
25844                 size : 'sm',
25845                 xtype: 'Button',
25846                 xns: Roo.bootstrap,
25847                 //glyphicon : id,
25848                 fa: id,
25849                 cmd : id || cmd,
25850                 enableToggle:toggle !== false,
25851                 html : html || '',
25852                 pressed : toggle ? false : null,
25853                 listeners : {}
25854             };
25855             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25856                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25857             };
25858             children.push(a);
25859             return a;
25860        }
25861        
25862     //    var cb_box = function...
25863         
25864         var style = {
25865                 xtype: 'Button',
25866                 size : 'sm',
25867                 xns: Roo.bootstrap,
25868                 fa : 'font',
25869                 //html : 'submit'
25870                 menu : {
25871                     xtype: 'Menu',
25872                     xns: Roo.bootstrap,
25873                     items:  []
25874                 }
25875         };
25876         Roo.each(this.formats, function(f) {
25877             style.menu.items.push({
25878                 xtype :'MenuItem',
25879                 xns: Roo.bootstrap,
25880                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25881                 tagname : f,
25882                 listeners : {
25883                     click : function()
25884                     {
25885                         editorcore.insertTag(this.tagname);
25886                         editor.focus();
25887                     }
25888                 }
25889                 
25890             });
25891         });
25892         children.push(style);   
25893         
25894         btn('bold',false,true);
25895         btn('italic',false,true);
25896         btn('align-left', 'justifyleft',true);
25897         btn('align-center', 'justifycenter',true);
25898         btn('align-right' , 'justifyright',true);
25899         btn('link', false, false, function(btn) {
25900             //Roo.log("create link?");
25901             var url = prompt(this.createLinkText, this.defaultLinkValue);
25902             if(url && url != 'http:/'+'/'){
25903                 this.editorcore.relayCmd('createlink', url);
25904             }
25905         }),
25906         btn('list','insertunorderedlist',true);
25907         btn('pencil', false,true, function(btn){
25908                 Roo.log(this);
25909                 this.toggleSourceEdit(btn.pressed);
25910         });
25911         
25912         if (this.editor.btns.length > 0) {
25913             for (var i = 0; i<this.editor.btns.length; i++) {
25914                 children.push(this.editor.btns[i]);
25915             }
25916         }
25917         
25918         /*
25919         var cog = {
25920                 xtype: 'Button',
25921                 size : 'sm',
25922                 xns: Roo.bootstrap,
25923                 glyphicon : 'cog',
25924                 //html : 'submit'
25925                 menu : {
25926                     xtype: 'Menu',
25927                     xns: Roo.bootstrap,
25928                     items:  []
25929                 }
25930         };
25931         
25932         cog.menu.items.push({
25933             xtype :'MenuItem',
25934             xns: Roo.bootstrap,
25935             html : Clean styles,
25936             tagname : f,
25937             listeners : {
25938                 click : function()
25939                 {
25940                     editorcore.insertTag(this.tagname);
25941                     editor.focus();
25942                 }
25943             }
25944             
25945         });
25946        */
25947         
25948          
25949        this.xtype = 'NavSimplebar';
25950         
25951         for(var i=0;i< children.length;i++) {
25952             
25953             this.buttons.add(this.addxtypeChild(children[i]));
25954             
25955         }
25956         
25957         editor.on('editorevent', this.updateToolbar, this);
25958     },
25959     onBtnClick : function(id)
25960     {
25961        this.editorcore.relayCmd(id);
25962        this.editorcore.focus();
25963     },
25964     
25965     /**
25966      * Protected method that will not generally be called directly. It triggers
25967      * a toolbar update by reading the markup state of the current selection in the editor.
25968      */
25969     updateToolbar: function(){
25970
25971         if(!this.editorcore.activated){
25972             this.editor.onFirstFocus(); // is this neeed?
25973             return;
25974         }
25975
25976         var btns = this.buttons; 
25977         var doc = this.editorcore.doc;
25978         btns.get('bold').setActive(doc.queryCommandState('bold'));
25979         btns.get('italic').setActive(doc.queryCommandState('italic'));
25980         //btns.get('underline').setActive(doc.queryCommandState('underline'));
25981         
25982         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
25983         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
25984         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
25985         
25986         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
25987         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
25988          /*
25989         
25990         var ans = this.editorcore.getAllAncestors();
25991         if (this.formatCombo) {
25992             
25993             
25994             var store = this.formatCombo.store;
25995             this.formatCombo.setValue("");
25996             for (var i =0; i < ans.length;i++) {
25997                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25998                     // select it..
25999                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26000                     break;
26001                 }
26002             }
26003         }
26004         
26005         
26006         
26007         // hides menus... - so this cant be on a menu...
26008         Roo.bootstrap.MenuMgr.hideAll();
26009         */
26010         Roo.bootstrap.MenuMgr.hideAll();
26011         //this.editorsyncValue();
26012     },
26013     onFirstFocus: function() {
26014         this.buttons.each(function(item){
26015            item.enable();
26016         });
26017     },
26018     toggleSourceEdit : function(sourceEditMode){
26019         
26020           
26021         if(sourceEditMode){
26022             Roo.log("disabling buttons");
26023            this.buttons.each( function(item){
26024                 if(item.cmd != 'pencil'){
26025                     item.disable();
26026                 }
26027             });
26028           
26029         }else{
26030             Roo.log("enabling buttons");
26031             if(this.editorcore.initialized){
26032                 this.buttons.each( function(item){
26033                     item.enable();
26034                 });
26035             }
26036             
26037         }
26038         Roo.log("calling toggole on editor");
26039         // tell the editor that it's been pressed..
26040         this.editor.toggleSourceEdit(sourceEditMode);
26041        
26042     }
26043 });
26044
26045
26046
26047
26048
26049 /**
26050  * @class Roo.bootstrap.Table.AbstractSelectionModel
26051  * @extends Roo.util.Observable
26052  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26053  * implemented by descendant classes.  This class should not be directly instantiated.
26054  * @constructor
26055  */
26056 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26057     this.locked = false;
26058     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26059 };
26060
26061
26062 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26063     /** @ignore Called by the grid automatically. Do not call directly. */
26064     init : function(grid){
26065         this.grid = grid;
26066         this.initEvents();
26067     },
26068
26069     /**
26070      * Locks the selections.
26071      */
26072     lock : function(){
26073         this.locked = true;
26074     },
26075
26076     /**
26077      * Unlocks the selections.
26078      */
26079     unlock : function(){
26080         this.locked = false;
26081     },
26082
26083     /**
26084      * Returns true if the selections are locked.
26085      * @return {Boolean}
26086      */
26087     isLocked : function(){
26088         return this.locked;
26089     },
26090     
26091     
26092     initEvents : function ()
26093     {
26094         
26095     }
26096 });
26097 /**
26098  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26099  * @class Roo.bootstrap.Table.RowSelectionModel
26100  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26101  * It supports multiple selections and keyboard selection/navigation. 
26102  * @constructor
26103  * @param {Object} config
26104  */
26105
26106 Roo.bootstrap.Table.RowSelectionModel = function(config){
26107     Roo.apply(this, config);
26108     this.selections = new Roo.util.MixedCollection(false, function(o){
26109         return o.id;
26110     });
26111
26112     this.last = false;
26113     this.lastActive = false;
26114
26115     this.addEvents({
26116         /**
26117              * @event selectionchange
26118              * Fires when the selection changes
26119              * @param {SelectionModel} this
26120              */
26121             "selectionchange" : true,
26122         /**
26123              * @event afterselectionchange
26124              * Fires after the selection changes (eg. by key press or clicking)
26125              * @param {SelectionModel} this
26126              */
26127             "afterselectionchange" : true,
26128         /**
26129              * @event beforerowselect
26130              * Fires when a row is selected being selected, return false to cancel.
26131              * @param {SelectionModel} this
26132              * @param {Number} rowIndex The selected index
26133              * @param {Boolean} keepExisting False if other selections will be cleared
26134              */
26135             "beforerowselect" : true,
26136         /**
26137              * @event rowselect
26138              * Fires when a row is selected.
26139              * @param {SelectionModel} this
26140              * @param {Number} rowIndex The selected index
26141              * @param {Roo.data.Record} r The record
26142              */
26143             "rowselect" : true,
26144         /**
26145              * @event rowdeselect
26146              * Fires when a row is deselected.
26147              * @param {SelectionModel} this
26148              * @param {Number} rowIndex The selected index
26149              */
26150         "rowdeselect" : true
26151     });
26152     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26153     this.locked = false;
26154  };
26155
26156 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26157     /**
26158      * @cfg {Boolean} singleSelect
26159      * True to allow selection of only one row at a time (defaults to false)
26160      */
26161     singleSelect : false,
26162
26163     // private
26164     initEvents : function()
26165     {
26166
26167         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26168         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26169         //}else{ // allow click to work like normal
26170          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26171         //}
26172         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26173         this.grid.on("rowclick", this.handleMouseDown, this);
26174         
26175         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26176             "up" : function(e){
26177                 if(!e.shiftKey){
26178                     this.selectPrevious(e.shiftKey);
26179                 }else if(this.last !== false && this.lastActive !== false){
26180                     var last = this.last;
26181                     this.selectRange(this.last,  this.lastActive-1);
26182                     this.grid.getView().focusRow(this.lastActive);
26183                     if(last !== false){
26184                         this.last = last;
26185                     }
26186                 }else{
26187                     this.selectFirstRow();
26188                 }
26189                 this.fireEvent("afterselectionchange", this);
26190             },
26191             "down" : function(e){
26192                 if(!e.shiftKey){
26193                     this.selectNext(e.shiftKey);
26194                 }else if(this.last !== false && this.lastActive !== false){
26195                     var last = this.last;
26196                     this.selectRange(this.last,  this.lastActive+1);
26197                     this.grid.getView().focusRow(this.lastActive);
26198                     if(last !== false){
26199                         this.last = last;
26200                     }
26201                 }else{
26202                     this.selectFirstRow();
26203                 }
26204                 this.fireEvent("afterselectionchange", this);
26205             },
26206             scope: this
26207         });
26208         this.grid.store.on('load', function(){
26209             this.selections.clear();
26210         },this);
26211         /*
26212         var view = this.grid.view;
26213         view.on("refresh", this.onRefresh, this);
26214         view.on("rowupdated", this.onRowUpdated, this);
26215         view.on("rowremoved", this.onRemove, this);
26216         */
26217     },
26218
26219     // private
26220     onRefresh : function()
26221     {
26222         var ds = this.grid.store, i, v = this.grid.view;
26223         var s = this.selections;
26224         s.each(function(r){
26225             if((i = ds.indexOfId(r.id)) != -1){
26226                 v.onRowSelect(i);
26227             }else{
26228                 s.remove(r);
26229             }
26230         });
26231     },
26232
26233     // private
26234     onRemove : function(v, index, r){
26235         this.selections.remove(r);
26236     },
26237
26238     // private
26239     onRowUpdated : function(v, index, r){
26240         if(this.isSelected(r)){
26241             v.onRowSelect(index);
26242         }
26243     },
26244
26245     /**
26246      * Select records.
26247      * @param {Array} records The records to select
26248      * @param {Boolean} keepExisting (optional) True to keep existing selections
26249      */
26250     selectRecords : function(records, keepExisting)
26251     {
26252         if(!keepExisting){
26253             this.clearSelections();
26254         }
26255             var ds = this.grid.store;
26256         for(var i = 0, len = records.length; i < len; i++){
26257             this.selectRow(ds.indexOf(records[i]), true);
26258         }
26259     },
26260
26261     /**
26262      * Gets the number of selected rows.
26263      * @return {Number}
26264      */
26265     getCount : function(){
26266         return this.selections.length;
26267     },
26268
26269     /**
26270      * Selects the first row in the grid.
26271      */
26272     selectFirstRow : function(){
26273         this.selectRow(0);
26274     },
26275
26276     /**
26277      * Select the last row.
26278      * @param {Boolean} keepExisting (optional) True to keep existing selections
26279      */
26280     selectLastRow : function(keepExisting){
26281         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26282         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26283     },
26284
26285     /**
26286      * Selects the row immediately following the last selected row.
26287      * @param {Boolean} keepExisting (optional) True to keep existing selections
26288      */
26289     selectNext : function(keepExisting)
26290     {
26291             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26292             this.selectRow(this.last+1, keepExisting);
26293             this.grid.getView().focusRow(this.last);
26294         }
26295     },
26296
26297     /**
26298      * Selects the row that precedes the last selected row.
26299      * @param {Boolean} keepExisting (optional) True to keep existing selections
26300      */
26301     selectPrevious : function(keepExisting){
26302         if(this.last){
26303             this.selectRow(this.last-1, keepExisting);
26304             this.grid.getView().focusRow(this.last);
26305         }
26306     },
26307
26308     /**
26309      * Returns the selected records
26310      * @return {Array} Array of selected records
26311      */
26312     getSelections : function(){
26313         return [].concat(this.selections.items);
26314     },
26315
26316     /**
26317      * Returns the first selected record.
26318      * @return {Record}
26319      */
26320     getSelected : function(){
26321         return this.selections.itemAt(0);
26322     },
26323
26324
26325     /**
26326      * Clears all selections.
26327      */
26328     clearSelections : function(fast)
26329     {
26330         if(this.locked) {
26331             return;
26332         }
26333         if(fast !== true){
26334                 var ds = this.grid.store;
26335             var s = this.selections;
26336             s.each(function(r){
26337                 this.deselectRow(ds.indexOfId(r.id));
26338             }, this);
26339             s.clear();
26340         }else{
26341             this.selections.clear();
26342         }
26343         this.last = false;
26344     },
26345
26346
26347     /**
26348      * Selects all rows.
26349      */
26350     selectAll : function(){
26351         if(this.locked) {
26352             return;
26353         }
26354         this.selections.clear();
26355         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26356             this.selectRow(i, true);
26357         }
26358     },
26359
26360     /**
26361      * Returns True if there is a selection.
26362      * @return {Boolean}
26363      */
26364     hasSelection : function(){
26365         return this.selections.length > 0;
26366     },
26367
26368     /**
26369      * Returns True if the specified row is selected.
26370      * @param {Number/Record} record The record or index of the record to check
26371      * @return {Boolean}
26372      */
26373     isSelected : function(index){
26374             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26375         return (r && this.selections.key(r.id) ? true : false);
26376     },
26377
26378     /**
26379      * Returns True if the specified record id is selected.
26380      * @param {String} id The id of record to check
26381      * @return {Boolean}
26382      */
26383     isIdSelected : function(id){
26384         return (this.selections.key(id) ? true : false);
26385     },
26386
26387
26388     // private
26389     handleMouseDBClick : function(e, t){
26390         
26391     },
26392     // private
26393     handleMouseDown : function(e, t)
26394     {
26395             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26396         if(this.isLocked() || rowIndex < 0 ){
26397             return;
26398         };
26399         if(e.shiftKey && this.last !== false){
26400             var last = this.last;
26401             this.selectRange(last, rowIndex, e.ctrlKey);
26402             this.last = last; // reset the last
26403             t.focus();
26404     
26405         }else{
26406             var isSelected = this.isSelected(rowIndex);
26407             //Roo.log("select row:" + rowIndex);
26408             if(isSelected){
26409                 this.deselectRow(rowIndex);
26410             } else {
26411                         this.selectRow(rowIndex, true);
26412             }
26413     
26414             /*
26415                 if(e.button !== 0 && isSelected){
26416                 alert('rowIndex 2: ' + rowIndex);
26417                     view.focusRow(rowIndex);
26418                 }else if(e.ctrlKey && isSelected){
26419                     this.deselectRow(rowIndex);
26420                 }else if(!isSelected){
26421                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26422                     view.focusRow(rowIndex);
26423                 }
26424             */
26425         }
26426         this.fireEvent("afterselectionchange", this);
26427     },
26428     // private
26429     handleDragableRowClick :  function(grid, rowIndex, e) 
26430     {
26431         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26432             this.selectRow(rowIndex, false);
26433             grid.view.focusRow(rowIndex);
26434              this.fireEvent("afterselectionchange", this);
26435         }
26436     },
26437     
26438     /**
26439      * Selects multiple rows.
26440      * @param {Array} rows Array of the indexes of the row to select
26441      * @param {Boolean} keepExisting (optional) True to keep existing selections
26442      */
26443     selectRows : function(rows, keepExisting){
26444         if(!keepExisting){
26445             this.clearSelections();
26446         }
26447         for(var i = 0, len = rows.length; i < len; i++){
26448             this.selectRow(rows[i], true);
26449         }
26450     },
26451
26452     /**
26453      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26454      * @param {Number} startRow The index of the first row in the range
26455      * @param {Number} endRow The index of the last row in the range
26456      * @param {Boolean} keepExisting (optional) True to retain existing selections
26457      */
26458     selectRange : function(startRow, endRow, keepExisting){
26459         if(this.locked) {
26460             return;
26461         }
26462         if(!keepExisting){
26463             this.clearSelections();
26464         }
26465         if(startRow <= endRow){
26466             for(var i = startRow; i <= endRow; i++){
26467                 this.selectRow(i, true);
26468             }
26469         }else{
26470             for(var i = startRow; i >= endRow; i--){
26471                 this.selectRow(i, true);
26472             }
26473         }
26474     },
26475
26476     /**
26477      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26478      * @param {Number} startRow The index of the first row in the range
26479      * @param {Number} endRow The index of the last row in the range
26480      */
26481     deselectRange : function(startRow, endRow, preventViewNotify){
26482         if(this.locked) {
26483             return;
26484         }
26485         for(var i = startRow; i <= endRow; i++){
26486             this.deselectRow(i, preventViewNotify);
26487         }
26488     },
26489
26490     /**
26491      * Selects a row.
26492      * @param {Number} row The index of the row to select
26493      * @param {Boolean} keepExisting (optional) True to keep existing selections
26494      */
26495     selectRow : function(index, keepExisting, preventViewNotify)
26496     {
26497             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26498             return;
26499         }
26500         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26501             if(!keepExisting || this.singleSelect){
26502                 this.clearSelections();
26503             }
26504             
26505             var r = this.grid.store.getAt(index);
26506             //console.log('selectRow - record id :' + r.id);
26507             
26508             this.selections.add(r);
26509             this.last = this.lastActive = index;
26510             if(!preventViewNotify){
26511                 var proxy = new Roo.Element(
26512                                 this.grid.getRowDom(index)
26513                 );
26514                 proxy.addClass('bg-info info');
26515             }
26516             this.fireEvent("rowselect", this, index, r);
26517             this.fireEvent("selectionchange", this);
26518         }
26519     },
26520
26521     /**
26522      * Deselects a row.
26523      * @param {Number} row The index of the row to deselect
26524      */
26525     deselectRow : function(index, preventViewNotify)
26526     {
26527         if(this.locked) {
26528             return;
26529         }
26530         if(this.last == index){
26531             this.last = false;
26532         }
26533         if(this.lastActive == index){
26534             this.lastActive = false;
26535         }
26536         
26537         var r = this.grid.store.getAt(index);
26538         if (!r) {
26539             return;
26540         }
26541         
26542         this.selections.remove(r);
26543         //.console.log('deselectRow - record id :' + r.id);
26544         if(!preventViewNotify){
26545         
26546             var proxy = new Roo.Element(
26547                 this.grid.getRowDom(index)
26548             );
26549             proxy.removeClass('bg-info info');
26550         }
26551         this.fireEvent("rowdeselect", this, index);
26552         this.fireEvent("selectionchange", this);
26553     },
26554
26555     // private
26556     restoreLast : function(){
26557         if(this._last){
26558             this.last = this._last;
26559         }
26560     },
26561
26562     // private
26563     acceptsNav : function(row, col, cm){
26564         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26565     },
26566
26567     // private
26568     onEditorKey : function(field, e){
26569         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26570         if(k == e.TAB){
26571             e.stopEvent();
26572             ed.completeEdit();
26573             if(e.shiftKey){
26574                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26575             }else{
26576                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26577             }
26578         }else if(k == e.ENTER && !e.ctrlKey){
26579             e.stopEvent();
26580             ed.completeEdit();
26581             if(e.shiftKey){
26582                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26583             }else{
26584                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26585             }
26586         }else if(k == e.ESC){
26587             ed.cancelEdit();
26588         }
26589         if(newCell){
26590             g.startEditing(newCell[0], newCell[1]);
26591         }
26592     }
26593 });
26594 /*
26595  * Based on:
26596  * Ext JS Library 1.1.1
26597  * Copyright(c) 2006-2007, Ext JS, LLC.
26598  *
26599  * Originally Released Under LGPL - original licence link has changed is not relivant.
26600  *
26601  * Fork - LGPL
26602  * <script type="text/javascript">
26603  */
26604  
26605 /**
26606  * @class Roo.bootstrap.PagingToolbar
26607  * @extends Roo.bootstrap.NavSimplebar
26608  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26609  * @constructor
26610  * Create a new PagingToolbar
26611  * @param {Object} config The config object
26612  * @param {Roo.data.Store} store
26613  */
26614 Roo.bootstrap.PagingToolbar = function(config)
26615 {
26616     // old args format still supported... - xtype is prefered..
26617         // created from xtype...
26618     
26619     this.ds = config.dataSource;
26620     
26621     if (config.store && !this.ds) {
26622         this.store= Roo.factory(config.store, Roo.data);
26623         this.ds = this.store;
26624         this.ds.xmodule = this.xmodule || false;
26625     }
26626     
26627     this.toolbarItems = [];
26628     if (config.items) {
26629         this.toolbarItems = config.items;
26630     }
26631     
26632     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26633     
26634     this.cursor = 0;
26635     
26636     if (this.ds) { 
26637         this.bind(this.ds);
26638     }
26639     
26640     if (Roo.bootstrap.version == 4) {
26641         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26642     } else {
26643         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26644     }
26645     
26646 };
26647
26648 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26649     /**
26650      * @cfg {Roo.data.Store} dataSource
26651      * The underlying data store providing the paged data
26652      */
26653     /**
26654      * @cfg {String/HTMLElement/Element} container
26655      * container The id or element that will contain the toolbar
26656      */
26657     /**
26658      * @cfg {Boolean} displayInfo
26659      * True to display the displayMsg (defaults to false)
26660      */
26661     /**
26662      * @cfg {Number} pageSize
26663      * The number of records to display per page (defaults to 20)
26664      */
26665     pageSize: 20,
26666     /**
26667      * @cfg {String} displayMsg
26668      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26669      */
26670     displayMsg : 'Displaying {0} - {1} of {2}',
26671     /**
26672      * @cfg {String} emptyMsg
26673      * The message to display when no records are found (defaults to "No data to display")
26674      */
26675     emptyMsg : 'No data to display',
26676     /**
26677      * Customizable piece of the default paging text (defaults to "Page")
26678      * @type String
26679      */
26680     beforePageText : "Page",
26681     /**
26682      * Customizable piece of the default paging text (defaults to "of %0")
26683      * @type String
26684      */
26685     afterPageText : "of {0}",
26686     /**
26687      * Customizable piece of the default paging text (defaults to "First Page")
26688      * @type String
26689      */
26690     firstText : "First Page",
26691     /**
26692      * Customizable piece of the default paging text (defaults to "Previous Page")
26693      * @type String
26694      */
26695     prevText : "Previous Page",
26696     /**
26697      * Customizable piece of the default paging text (defaults to "Next Page")
26698      * @type String
26699      */
26700     nextText : "Next Page",
26701     /**
26702      * Customizable piece of the default paging text (defaults to "Last Page")
26703      * @type String
26704      */
26705     lastText : "Last Page",
26706     /**
26707      * Customizable piece of the default paging text (defaults to "Refresh")
26708      * @type String
26709      */
26710     refreshText : "Refresh",
26711
26712     buttons : false,
26713     // private
26714     onRender : function(ct, position) 
26715     {
26716         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26717         this.navgroup.parentId = this.id;
26718         this.navgroup.onRender(this.el, null);
26719         // add the buttons to the navgroup
26720         
26721         if(this.displayInfo){
26722             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26723             this.displayEl = this.el.select('.x-paging-info', true).first();
26724 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26725 //            this.displayEl = navel.el.select('span',true).first();
26726         }
26727         
26728         var _this = this;
26729         
26730         if(this.buttons){
26731             Roo.each(_this.buttons, function(e){ // this might need to use render????
26732                Roo.factory(e).render(_this.el);
26733             });
26734         }
26735             
26736         Roo.each(_this.toolbarItems, function(e) {
26737             _this.navgroup.addItem(e);
26738         });
26739         
26740         
26741         this.first = this.navgroup.addItem({
26742             tooltip: this.firstText,
26743             cls: "prev btn-outline-secondary",
26744             html : ' <i class="fa fa-step-backward"></i>',
26745             disabled: true,
26746             preventDefault: true,
26747             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26748         });
26749         
26750         this.prev =  this.navgroup.addItem({
26751             tooltip: this.prevText,
26752             cls: "prev btn-outline-secondary",
26753             html : ' <i class="fa fa-backward"></i>',
26754             disabled: true,
26755             preventDefault: true,
26756             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26757         });
26758     //this.addSeparator();
26759         
26760         
26761         var field = this.navgroup.addItem( {
26762             tagtype : 'span',
26763             cls : 'x-paging-position  btn-outline-secondary',
26764              disabled: true,
26765             html : this.beforePageText  +
26766                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26767                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26768          } ); //?? escaped?
26769         
26770         this.field = field.el.select('input', true).first();
26771         this.field.on("keydown", this.onPagingKeydown, this);
26772         this.field.on("focus", function(){this.dom.select();});
26773     
26774     
26775         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26776         //this.field.setHeight(18);
26777         //this.addSeparator();
26778         this.next = this.navgroup.addItem({
26779             tooltip: this.nextText,
26780             cls: "next btn-outline-secondary",
26781             html : ' <i class="fa fa-forward"></i>',
26782             disabled: true,
26783             preventDefault: true,
26784             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26785         });
26786         this.last = this.navgroup.addItem({
26787             tooltip: this.lastText,
26788             html : ' <i class="fa fa-step-forward"></i>',
26789             cls: "next btn-outline-secondary",
26790             disabled: true,
26791             preventDefault: true,
26792             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26793         });
26794     //this.addSeparator();
26795         this.loading = this.navgroup.addItem({
26796             tooltip: this.refreshText,
26797             cls: "btn-outline-secondary",
26798             html : ' <i class="fa fa-refresh"></i>',
26799             preventDefault: true,
26800             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26801         });
26802         
26803     },
26804
26805     // private
26806     updateInfo : function(){
26807         if(this.displayEl){
26808             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26809             var msg = count == 0 ?
26810                 this.emptyMsg :
26811                 String.format(
26812                     this.displayMsg,
26813                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26814                 );
26815             this.displayEl.update(msg);
26816         }
26817     },
26818
26819     // private
26820     onLoad : function(ds, r, o)
26821     {
26822         this.cursor = o.params.start ? o.params.start : 0;
26823         
26824         var d = this.getPageData(),
26825             ap = d.activePage,
26826             ps = d.pages;
26827         
26828         
26829         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26830         this.field.dom.value = ap;
26831         this.first.setDisabled(ap == 1);
26832         this.prev.setDisabled(ap == 1);
26833         this.next.setDisabled(ap == ps);
26834         this.last.setDisabled(ap == ps);
26835         this.loading.enable();
26836         this.updateInfo();
26837     },
26838
26839     // private
26840     getPageData : function(){
26841         var total = this.ds.getTotalCount();
26842         return {
26843             total : total,
26844             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26845             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26846         };
26847     },
26848
26849     // private
26850     onLoadError : function(){
26851         this.loading.enable();
26852     },
26853
26854     // private
26855     onPagingKeydown : function(e){
26856         var k = e.getKey();
26857         var d = this.getPageData();
26858         if(k == e.RETURN){
26859             var v = this.field.dom.value, pageNum;
26860             if(!v || isNaN(pageNum = parseInt(v, 10))){
26861                 this.field.dom.value = d.activePage;
26862                 return;
26863             }
26864             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
26865             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26866             e.stopEvent();
26867         }
26868         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))
26869         {
26870           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
26871           this.field.dom.value = pageNum;
26872           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
26873           e.stopEvent();
26874         }
26875         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
26876         {
26877           var v = this.field.dom.value, pageNum; 
26878           var increment = (e.shiftKey) ? 10 : 1;
26879           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
26880                 increment *= -1;
26881           }
26882           if(!v || isNaN(pageNum = parseInt(v, 10))) {
26883             this.field.dom.value = d.activePage;
26884             return;
26885           }
26886           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
26887           {
26888             this.field.dom.value = parseInt(v, 10) + increment;
26889             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
26890             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
26891           }
26892           e.stopEvent();
26893         }
26894     },
26895
26896     // private
26897     beforeLoad : function(){
26898         if(this.loading){
26899             this.loading.disable();
26900         }
26901     },
26902
26903     // private
26904     onClick : function(which){
26905         
26906         var ds = this.ds;
26907         if (!ds) {
26908             return;
26909         }
26910         
26911         switch(which){
26912             case "first":
26913                 ds.load({params:{start: 0, limit: this.pageSize}});
26914             break;
26915             case "prev":
26916                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
26917             break;
26918             case "next":
26919                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
26920             break;
26921             case "last":
26922                 var total = ds.getTotalCount();
26923                 var extra = total % this.pageSize;
26924                 var lastStart = extra ? (total - extra) : total-this.pageSize;
26925                 ds.load({params:{start: lastStart, limit: this.pageSize}});
26926             break;
26927             case "refresh":
26928                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
26929             break;
26930         }
26931     },
26932
26933     /**
26934      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
26935      * @param {Roo.data.Store} store The data store to unbind
26936      */
26937     unbind : function(ds){
26938         ds.un("beforeload", this.beforeLoad, this);
26939         ds.un("load", this.onLoad, this);
26940         ds.un("loadexception", this.onLoadError, this);
26941         ds.un("remove", this.updateInfo, this);
26942         ds.un("add", this.updateInfo, this);
26943         this.ds = undefined;
26944     },
26945
26946     /**
26947      * Binds the paging toolbar to the specified {@link Roo.data.Store}
26948      * @param {Roo.data.Store} store The data store to bind
26949      */
26950     bind : function(ds){
26951         ds.on("beforeload", this.beforeLoad, this);
26952         ds.on("load", this.onLoad, this);
26953         ds.on("loadexception", this.onLoadError, this);
26954         ds.on("remove", this.updateInfo, this);
26955         ds.on("add", this.updateInfo, this);
26956         this.ds = ds;
26957     }
26958 });/*
26959  * - LGPL
26960  *
26961  * element
26962  * 
26963  */
26964
26965 /**
26966  * @class Roo.bootstrap.MessageBar
26967  * @extends Roo.bootstrap.Component
26968  * Bootstrap MessageBar class
26969  * @cfg {String} html contents of the MessageBar
26970  * @cfg {String} weight (info | success | warning | danger) default info
26971  * @cfg {String} beforeClass insert the bar before the given class
26972  * @cfg {Boolean} closable (true | false) default false
26973  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
26974  * 
26975  * @constructor
26976  * Create a new Element
26977  * @param {Object} config The config object
26978  */
26979
26980 Roo.bootstrap.MessageBar = function(config){
26981     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
26982 };
26983
26984 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
26985     
26986     html: '',
26987     weight: 'info',
26988     closable: false,
26989     fixed: false,
26990     beforeClass: 'bootstrap-sticky-wrap',
26991     
26992     getAutoCreate : function(){
26993         
26994         var cfg = {
26995             tag: 'div',
26996             cls: 'alert alert-dismissable alert-' + this.weight,
26997             cn: [
26998                 {
26999                     tag: 'span',
27000                     cls: 'message',
27001                     html: this.html || ''
27002                 }
27003             ]
27004         };
27005         
27006         if(this.fixed){
27007             cfg.cls += ' alert-messages-fixed';
27008         }
27009         
27010         if(this.closable){
27011             cfg.cn.push({
27012                 tag: 'button',
27013                 cls: 'close',
27014                 html: 'x'
27015             });
27016         }
27017         
27018         return cfg;
27019     },
27020     
27021     onRender : function(ct, position)
27022     {
27023         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27024         
27025         if(!this.el){
27026             var cfg = Roo.apply({},  this.getAutoCreate());
27027             cfg.id = Roo.id();
27028             
27029             if (this.cls) {
27030                 cfg.cls += ' ' + this.cls;
27031             }
27032             if (this.style) {
27033                 cfg.style = this.style;
27034             }
27035             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27036             
27037             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27038         }
27039         
27040         this.el.select('>button.close').on('click', this.hide, this);
27041         
27042     },
27043     
27044     show : function()
27045     {
27046         if (!this.rendered) {
27047             this.render();
27048         }
27049         
27050         this.el.show();
27051         
27052         this.fireEvent('show', this);
27053         
27054     },
27055     
27056     hide : function()
27057     {
27058         if (!this.rendered) {
27059             this.render();
27060         }
27061         
27062         this.el.hide();
27063         
27064         this.fireEvent('hide', this);
27065     },
27066     
27067     update : function()
27068     {
27069 //        var e = this.el.dom.firstChild;
27070 //        
27071 //        if(this.closable){
27072 //            e = e.nextSibling;
27073 //        }
27074 //        
27075 //        e.data = this.html || '';
27076
27077         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27078     }
27079    
27080 });
27081
27082  
27083
27084      /*
27085  * - LGPL
27086  *
27087  * Graph
27088  * 
27089  */
27090
27091
27092 /**
27093  * @class Roo.bootstrap.Graph
27094  * @extends Roo.bootstrap.Component
27095  * Bootstrap Graph class
27096 > Prameters
27097  -sm {number} sm 4
27098  -md {number} md 5
27099  @cfg {String} graphtype  bar | vbar | pie
27100  @cfg {number} g_x coodinator | centre x (pie)
27101  @cfg {number} g_y coodinator | centre y (pie)
27102  @cfg {number} g_r radius (pie)
27103  @cfg {number} g_height height of the chart (respected by all elements in the set)
27104  @cfg {number} g_width width of the chart (respected by all elements in the set)
27105  @cfg {Object} title The title of the chart
27106     
27107  -{Array}  values
27108  -opts (object) options for the chart 
27109      o {
27110      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27111      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27112      o vgutter (number)
27113      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.
27114      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27115      o to
27116      o stretch (boolean)
27117      o }
27118  -opts (object) options for the pie
27119      o{
27120      o cut
27121      o startAngle (number)
27122      o endAngle (number)
27123      } 
27124  *
27125  * @constructor
27126  * Create a new Input
27127  * @param {Object} config The config object
27128  */
27129
27130 Roo.bootstrap.Graph = function(config){
27131     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27132     
27133     this.addEvents({
27134         // img events
27135         /**
27136          * @event click
27137          * The img click event for the img.
27138          * @param {Roo.EventObject} e
27139          */
27140         "click" : true
27141     });
27142 };
27143
27144 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27145     
27146     sm: 4,
27147     md: 5,
27148     graphtype: 'bar',
27149     g_height: 250,
27150     g_width: 400,
27151     g_x: 50,
27152     g_y: 50,
27153     g_r: 30,
27154     opts:{
27155         //g_colors: this.colors,
27156         g_type: 'soft',
27157         g_gutter: '20%'
27158
27159     },
27160     title : false,
27161
27162     getAutoCreate : function(){
27163         
27164         var cfg = {
27165             tag: 'div',
27166             html : null
27167         };
27168         
27169         
27170         return  cfg;
27171     },
27172
27173     onRender : function(ct,position){
27174         
27175         
27176         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27177         
27178         if (typeof(Raphael) == 'undefined') {
27179             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27180             return;
27181         }
27182         
27183         this.raphael = Raphael(this.el.dom);
27184         
27185                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27186                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27187                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27188                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27189                 /*
27190                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27191                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27192                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27193                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27194                 
27195                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27196                 r.barchart(330, 10, 300, 220, data1);
27197                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27198                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27199                 */
27200                 
27201                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27202                 // r.barchart(30, 30, 560, 250,  xdata, {
27203                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27204                 //     axis : "0 0 1 1",
27205                 //     axisxlabels :  xdata
27206                 //     //yvalues : cols,
27207                    
27208                 // });
27209 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27210 //        
27211 //        this.load(null,xdata,{
27212 //                axis : "0 0 1 1",
27213 //                axisxlabels :  xdata
27214 //                });
27215
27216     },
27217
27218     load : function(graphtype,xdata,opts)
27219     {
27220         this.raphael.clear();
27221         if(!graphtype) {
27222             graphtype = this.graphtype;
27223         }
27224         if(!opts){
27225             opts = this.opts;
27226         }
27227         var r = this.raphael,
27228             fin = function () {
27229                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27230             },
27231             fout = function () {
27232                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27233             },
27234             pfin = function() {
27235                 this.sector.stop();
27236                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27237
27238                 if (this.label) {
27239                     this.label[0].stop();
27240                     this.label[0].attr({ r: 7.5 });
27241                     this.label[1].attr({ "font-weight": 800 });
27242                 }
27243             },
27244             pfout = function() {
27245                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27246
27247                 if (this.label) {
27248                     this.label[0].animate({ r: 5 }, 500, "bounce");
27249                     this.label[1].attr({ "font-weight": 400 });
27250                 }
27251             };
27252
27253         switch(graphtype){
27254             case 'bar':
27255                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27256                 break;
27257             case 'hbar':
27258                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27259                 break;
27260             case 'pie':
27261 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27262 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27263 //            
27264                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27265                 
27266                 break;
27267
27268         }
27269         
27270         if(this.title){
27271             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27272         }
27273         
27274     },
27275     
27276     setTitle: function(o)
27277     {
27278         this.title = o;
27279     },
27280     
27281     initEvents: function() {
27282         
27283         if(!this.href){
27284             this.el.on('click', this.onClick, this);
27285         }
27286     },
27287     
27288     onClick : function(e)
27289     {
27290         Roo.log('img onclick');
27291         this.fireEvent('click', this, e);
27292     }
27293    
27294 });
27295
27296  
27297 /*
27298  * - LGPL
27299  *
27300  * numberBox
27301  * 
27302  */
27303 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27304
27305 /**
27306  * @class Roo.bootstrap.dash.NumberBox
27307  * @extends Roo.bootstrap.Component
27308  * Bootstrap NumberBox class
27309  * @cfg {String} headline Box headline
27310  * @cfg {String} content Box content
27311  * @cfg {String} icon Box icon
27312  * @cfg {String} footer Footer text
27313  * @cfg {String} fhref Footer href
27314  * 
27315  * @constructor
27316  * Create a new NumberBox
27317  * @param {Object} config The config object
27318  */
27319
27320
27321 Roo.bootstrap.dash.NumberBox = function(config){
27322     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27323     
27324 };
27325
27326 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27327     
27328     headline : '',
27329     content : '',
27330     icon : '',
27331     footer : '',
27332     fhref : '',
27333     ficon : '',
27334     
27335     getAutoCreate : function(){
27336         
27337         var cfg = {
27338             tag : 'div',
27339             cls : 'small-box ',
27340             cn : [
27341                 {
27342                     tag : 'div',
27343                     cls : 'inner',
27344                     cn :[
27345                         {
27346                             tag : 'h3',
27347                             cls : 'roo-headline',
27348                             html : this.headline
27349                         },
27350                         {
27351                             tag : 'p',
27352                             cls : 'roo-content',
27353                             html : this.content
27354                         }
27355                     ]
27356                 }
27357             ]
27358         };
27359         
27360         if(this.icon){
27361             cfg.cn.push({
27362                 tag : 'div',
27363                 cls : 'icon',
27364                 cn :[
27365                     {
27366                         tag : 'i',
27367                         cls : 'ion ' + this.icon
27368                     }
27369                 ]
27370             });
27371         }
27372         
27373         if(this.footer){
27374             var footer = {
27375                 tag : 'a',
27376                 cls : 'small-box-footer',
27377                 href : this.fhref || '#',
27378                 html : this.footer
27379             };
27380             
27381             cfg.cn.push(footer);
27382             
27383         }
27384         
27385         return  cfg;
27386     },
27387
27388     onRender : function(ct,position){
27389         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27390
27391
27392        
27393                 
27394     },
27395
27396     setHeadline: function (value)
27397     {
27398         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27399     },
27400     
27401     setFooter: function (value, href)
27402     {
27403         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27404         
27405         if(href){
27406             this.el.select('a.small-box-footer',true).first().attr('href', href);
27407         }
27408         
27409     },
27410
27411     setContent: function (value)
27412     {
27413         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27414     },
27415
27416     initEvents: function() 
27417     {   
27418         
27419     }
27420     
27421 });
27422
27423  
27424 /*
27425  * - LGPL
27426  *
27427  * TabBox
27428  * 
27429  */
27430 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27431
27432 /**
27433  * @class Roo.bootstrap.dash.TabBox
27434  * @extends Roo.bootstrap.Component
27435  * Bootstrap TabBox class
27436  * @cfg {String} title Title of the TabBox
27437  * @cfg {String} icon Icon of the TabBox
27438  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27439  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27440  * 
27441  * @constructor
27442  * Create a new TabBox
27443  * @param {Object} config The config object
27444  */
27445
27446
27447 Roo.bootstrap.dash.TabBox = function(config){
27448     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27449     this.addEvents({
27450         // raw events
27451         /**
27452          * @event addpane
27453          * When a pane is added
27454          * @param {Roo.bootstrap.dash.TabPane} pane
27455          */
27456         "addpane" : true,
27457         /**
27458          * @event activatepane
27459          * When a pane is activated
27460          * @param {Roo.bootstrap.dash.TabPane} pane
27461          */
27462         "activatepane" : true
27463         
27464          
27465     });
27466     
27467     this.panes = [];
27468 };
27469
27470 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27471
27472     title : '',
27473     icon : false,
27474     showtabs : true,
27475     tabScrollable : false,
27476     
27477     getChildContainer : function()
27478     {
27479         return this.el.select('.tab-content', true).first();
27480     },
27481     
27482     getAutoCreate : function(){
27483         
27484         var header = {
27485             tag: 'li',
27486             cls: 'pull-left header',
27487             html: this.title,
27488             cn : []
27489         };
27490         
27491         if(this.icon){
27492             header.cn.push({
27493                 tag: 'i',
27494                 cls: 'fa ' + this.icon
27495             });
27496         }
27497         
27498         var h = {
27499             tag: 'ul',
27500             cls: 'nav nav-tabs pull-right',
27501             cn: [
27502                 header
27503             ]
27504         };
27505         
27506         if(this.tabScrollable){
27507             h = {
27508                 tag: 'div',
27509                 cls: 'tab-header',
27510                 cn: [
27511                     {
27512                         tag: 'ul',
27513                         cls: 'nav nav-tabs pull-right',
27514                         cn: [
27515                             header
27516                         ]
27517                     }
27518                 ]
27519             };
27520         }
27521         
27522         var cfg = {
27523             tag: 'div',
27524             cls: 'nav-tabs-custom',
27525             cn: [
27526                 h,
27527                 {
27528                     tag: 'div',
27529                     cls: 'tab-content no-padding',
27530                     cn: []
27531                 }
27532             ]
27533         };
27534
27535         return  cfg;
27536     },
27537     initEvents : function()
27538     {
27539         //Roo.log('add add pane handler');
27540         this.on('addpane', this.onAddPane, this);
27541     },
27542      /**
27543      * Updates the box title
27544      * @param {String} html to set the title to.
27545      */
27546     setTitle : function(value)
27547     {
27548         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27549     },
27550     onAddPane : function(pane)
27551     {
27552         this.panes.push(pane);
27553         //Roo.log('addpane');
27554         //Roo.log(pane);
27555         // tabs are rendere left to right..
27556         if(!this.showtabs){
27557             return;
27558         }
27559         
27560         var ctr = this.el.select('.nav-tabs', true).first();
27561          
27562          
27563         var existing = ctr.select('.nav-tab',true);
27564         var qty = existing.getCount();;
27565         
27566         
27567         var tab = ctr.createChild({
27568             tag : 'li',
27569             cls : 'nav-tab' + (qty ? '' : ' active'),
27570             cn : [
27571                 {
27572                     tag : 'a',
27573                     href:'#',
27574                     html : pane.title
27575                 }
27576             ]
27577         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27578         pane.tab = tab;
27579         
27580         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27581         if (!qty) {
27582             pane.el.addClass('active');
27583         }
27584         
27585                 
27586     },
27587     onTabClick : function(ev,un,ob,pane)
27588     {
27589         //Roo.log('tab - prev default');
27590         ev.preventDefault();
27591         
27592         
27593         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27594         pane.tab.addClass('active');
27595         //Roo.log(pane.title);
27596         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27597         // technically we should have a deactivate event.. but maybe add later.
27598         // and it should not de-activate the selected tab...
27599         this.fireEvent('activatepane', pane);
27600         pane.el.addClass('active');
27601         pane.fireEvent('activate');
27602         
27603         
27604     },
27605     
27606     getActivePane : function()
27607     {
27608         var r = false;
27609         Roo.each(this.panes, function(p) {
27610             if(p.el.hasClass('active')){
27611                 r = p;
27612                 return false;
27613             }
27614             
27615             return;
27616         });
27617         
27618         return r;
27619     }
27620     
27621     
27622 });
27623
27624  
27625 /*
27626  * - LGPL
27627  *
27628  * Tab pane
27629  * 
27630  */
27631 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27632 /**
27633  * @class Roo.bootstrap.TabPane
27634  * @extends Roo.bootstrap.Component
27635  * Bootstrap TabPane class
27636  * @cfg {Boolean} active (false | true) Default false
27637  * @cfg {String} title title of panel
27638
27639  * 
27640  * @constructor
27641  * Create a new TabPane
27642  * @param {Object} config The config object
27643  */
27644
27645 Roo.bootstrap.dash.TabPane = function(config){
27646     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27647     
27648     this.addEvents({
27649         // raw events
27650         /**
27651          * @event activate
27652          * When a pane is activated
27653          * @param {Roo.bootstrap.dash.TabPane} pane
27654          */
27655         "activate" : true
27656          
27657     });
27658 };
27659
27660 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27661     
27662     active : false,
27663     title : '',
27664     
27665     // the tabBox that this is attached to.
27666     tab : false,
27667      
27668     getAutoCreate : function() 
27669     {
27670         var cfg = {
27671             tag: 'div',
27672             cls: 'tab-pane'
27673         };
27674         
27675         if(this.active){
27676             cfg.cls += ' active';
27677         }
27678         
27679         return cfg;
27680     },
27681     initEvents  : function()
27682     {
27683         //Roo.log('trigger add pane handler');
27684         this.parent().fireEvent('addpane', this)
27685     },
27686     
27687      /**
27688      * Updates the tab title 
27689      * @param {String} html to set the title to.
27690      */
27691     setTitle: function(str)
27692     {
27693         if (!this.tab) {
27694             return;
27695         }
27696         this.title = str;
27697         this.tab.select('a', true).first().dom.innerHTML = str;
27698         
27699     }
27700     
27701     
27702     
27703 });
27704
27705  
27706
27707
27708  /*
27709  * - LGPL
27710  *
27711  * menu
27712  * 
27713  */
27714 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27715
27716 /**
27717  * @class Roo.bootstrap.menu.Menu
27718  * @extends Roo.bootstrap.Component
27719  * Bootstrap Menu class - container for Menu
27720  * @cfg {String} html Text of the menu
27721  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27722  * @cfg {String} icon Font awesome icon
27723  * @cfg {String} pos Menu align to (top | bottom) default bottom
27724  * 
27725  * 
27726  * @constructor
27727  * Create a new Menu
27728  * @param {Object} config The config object
27729  */
27730
27731
27732 Roo.bootstrap.menu.Menu = function(config){
27733     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27734     
27735     this.addEvents({
27736         /**
27737          * @event beforeshow
27738          * Fires before this menu is displayed
27739          * @param {Roo.bootstrap.menu.Menu} this
27740          */
27741         beforeshow : true,
27742         /**
27743          * @event beforehide
27744          * Fires before this menu is hidden
27745          * @param {Roo.bootstrap.menu.Menu} this
27746          */
27747         beforehide : true,
27748         /**
27749          * @event show
27750          * Fires after this menu is displayed
27751          * @param {Roo.bootstrap.menu.Menu} this
27752          */
27753         show : true,
27754         /**
27755          * @event hide
27756          * Fires after this menu is hidden
27757          * @param {Roo.bootstrap.menu.Menu} this
27758          */
27759         hide : true,
27760         /**
27761          * @event click
27762          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27763          * @param {Roo.bootstrap.menu.Menu} this
27764          * @param {Roo.EventObject} e
27765          */
27766         click : true
27767     });
27768     
27769 };
27770
27771 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27772     
27773     submenu : false,
27774     html : '',
27775     weight : 'default',
27776     icon : false,
27777     pos : 'bottom',
27778     
27779     
27780     getChildContainer : function() {
27781         if(this.isSubMenu){
27782             return this.el;
27783         }
27784         
27785         return this.el.select('ul.dropdown-menu', true).first();  
27786     },
27787     
27788     getAutoCreate : function()
27789     {
27790         var text = [
27791             {
27792                 tag : 'span',
27793                 cls : 'roo-menu-text',
27794                 html : this.html
27795             }
27796         ];
27797         
27798         if(this.icon){
27799             text.unshift({
27800                 tag : 'i',
27801                 cls : 'fa ' + this.icon
27802             })
27803         }
27804         
27805         
27806         var cfg = {
27807             tag : 'div',
27808             cls : 'btn-group',
27809             cn : [
27810                 {
27811                     tag : 'button',
27812                     cls : 'dropdown-button btn btn-' + this.weight,
27813                     cn : text
27814                 },
27815                 {
27816                     tag : 'button',
27817                     cls : 'dropdown-toggle btn btn-' + this.weight,
27818                     cn : [
27819                         {
27820                             tag : 'span',
27821                             cls : 'caret'
27822                         }
27823                     ]
27824                 },
27825                 {
27826                     tag : 'ul',
27827                     cls : 'dropdown-menu'
27828                 }
27829             ]
27830             
27831         };
27832         
27833         if(this.pos == 'top'){
27834             cfg.cls += ' dropup';
27835         }
27836         
27837         if(this.isSubMenu){
27838             cfg = {
27839                 tag : 'ul',
27840                 cls : 'dropdown-menu'
27841             }
27842         }
27843         
27844         return cfg;
27845     },
27846     
27847     onRender : function(ct, position)
27848     {
27849         this.isSubMenu = ct.hasClass('dropdown-submenu');
27850         
27851         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
27852     },
27853     
27854     initEvents : function() 
27855     {
27856         if(this.isSubMenu){
27857             return;
27858         }
27859         
27860         this.hidden = true;
27861         
27862         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
27863         this.triggerEl.on('click', this.onTriggerPress, this);
27864         
27865         this.buttonEl = this.el.select('button.dropdown-button', true).first();
27866         this.buttonEl.on('click', this.onClick, this);
27867         
27868     },
27869     
27870     list : function()
27871     {
27872         if(this.isSubMenu){
27873             return this.el;
27874         }
27875         
27876         return this.el.select('ul.dropdown-menu', true).first();
27877     },
27878     
27879     onClick : function(e)
27880     {
27881         this.fireEvent("click", this, e);
27882     },
27883     
27884     onTriggerPress  : function(e)
27885     {   
27886         if (this.isVisible()) {
27887             this.hide();
27888         } else {
27889             this.show();
27890         }
27891     },
27892     
27893     isVisible : function(){
27894         return !this.hidden;
27895     },
27896     
27897     show : function()
27898     {
27899         this.fireEvent("beforeshow", this);
27900         
27901         this.hidden = false;
27902         this.el.addClass('open');
27903         
27904         Roo.get(document).on("mouseup", this.onMouseUp, this);
27905         
27906         this.fireEvent("show", this);
27907         
27908         
27909     },
27910     
27911     hide : function()
27912     {
27913         this.fireEvent("beforehide", this);
27914         
27915         this.hidden = true;
27916         this.el.removeClass('open');
27917         
27918         Roo.get(document).un("mouseup", this.onMouseUp);
27919         
27920         this.fireEvent("hide", this);
27921     },
27922     
27923     onMouseUp : function()
27924     {
27925         this.hide();
27926     }
27927     
27928 });
27929
27930  
27931  /*
27932  * - LGPL
27933  *
27934  * menu item
27935  * 
27936  */
27937 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27938
27939 /**
27940  * @class Roo.bootstrap.menu.Item
27941  * @extends Roo.bootstrap.Component
27942  * Bootstrap MenuItem class
27943  * @cfg {Boolean} submenu (true | false) default false
27944  * @cfg {String} html text of the item
27945  * @cfg {String} href the link
27946  * @cfg {Boolean} disable (true | false) default false
27947  * @cfg {Boolean} preventDefault (true | false) default true
27948  * @cfg {String} icon Font awesome icon
27949  * @cfg {String} pos Submenu align to (left | right) default right 
27950  * 
27951  * 
27952  * @constructor
27953  * Create a new Item
27954  * @param {Object} config The config object
27955  */
27956
27957
27958 Roo.bootstrap.menu.Item = function(config){
27959     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
27960     this.addEvents({
27961         /**
27962          * @event mouseover
27963          * Fires when the mouse is hovering over this menu
27964          * @param {Roo.bootstrap.menu.Item} this
27965          * @param {Roo.EventObject} e
27966          */
27967         mouseover : true,
27968         /**
27969          * @event mouseout
27970          * Fires when the mouse exits this menu
27971          * @param {Roo.bootstrap.menu.Item} this
27972          * @param {Roo.EventObject} e
27973          */
27974         mouseout : true,
27975         // raw events
27976         /**
27977          * @event click
27978          * The raw click event for the entire grid.
27979          * @param {Roo.EventObject} e
27980          */
27981         click : true
27982     });
27983 };
27984
27985 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
27986     
27987     submenu : false,
27988     href : '',
27989     html : '',
27990     preventDefault: true,
27991     disable : false,
27992     icon : false,
27993     pos : 'right',
27994     
27995     getAutoCreate : function()
27996     {
27997         var text = [
27998             {
27999                 tag : 'span',
28000                 cls : 'roo-menu-item-text',
28001                 html : this.html
28002             }
28003         ];
28004         
28005         if(this.icon){
28006             text.unshift({
28007                 tag : 'i',
28008                 cls : 'fa ' + this.icon
28009             })
28010         }
28011         
28012         var cfg = {
28013             tag : 'li',
28014             cn : [
28015                 {
28016                     tag : 'a',
28017                     href : this.href || '#',
28018                     cn : text
28019                 }
28020             ]
28021         };
28022         
28023         if(this.disable){
28024             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28025         }
28026         
28027         if(this.submenu){
28028             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28029             
28030             if(this.pos == 'left'){
28031                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28032             }
28033         }
28034         
28035         return cfg;
28036     },
28037     
28038     initEvents : function() 
28039     {
28040         this.el.on('mouseover', this.onMouseOver, this);
28041         this.el.on('mouseout', this.onMouseOut, this);
28042         
28043         this.el.select('a', true).first().on('click', this.onClick, this);
28044         
28045     },
28046     
28047     onClick : function(e)
28048     {
28049         if(this.preventDefault){
28050             e.preventDefault();
28051         }
28052         
28053         this.fireEvent("click", this, e);
28054     },
28055     
28056     onMouseOver : function(e)
28057     {
28058         if(this.submenu && this.pos == 'left'){
28059             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28060         }
28061         
28062         this.fireEvent("mouseover", this, e);
28063     },
28064     
28065     onMouseOut : function(e)
28066     {
28067         this.fireEvent("mouseout", this, e);
28068     }
28069 });
28070
28071  
28072
28073  /*
28074  * - LGPL
28075  *
28076  * menu separator
28077  * 
28078  */
28079 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28080
28081 /**
28082  * @class Roo.bootstrap.menu.Separator
28083  * @extends Roo.bootstrap.Component
28084  * Bootstrap Separator class
28085  * 
28086  * @constructor
28087  * Create a new Separator
28088  * @param {Object} config The config object
28089  */
28090
28091
28092 Roo.bootstrap.menu.Separator = function(config){
28093     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28094 };
28095
28096 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28097     
28098     getAutoCreate : function(){
28099         var cfg = {
28100             tag : 'li',
28101             cls: 'divider'
28102         };
28103         
28104         return cfg;
28105     }
28106    
28107 });
28108
28109  
28110
28111  /*
28112  * - LGPL
28113  *
28114  * Tooltip
28115  * 
28116  */
28117
28118 /**
28119  * @class Roo.bootstrap.Tooltip
28120  * Bootstrap Tooltip class
28121  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28122  * to determine which dom element triggers the tooltip.
28123  * 
28124  * It needs to add support for additional attributes like tooltip-position
28125  * 
28126  * @constructor
28127  * Create a new Toolti
28128  * @param {Object} config The config object
28129  */
28130
28131 Roo.bootstrap.Tooltip = function(config){
28132     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28133     
28134     this.alignment = Roo.bootstrap.Tooltip.alignment;
28135     
28136     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28137         this.alignment = config.alignment;
28138     }
28139     
28140 };
28141
28142 Roo.apply(Roo.bootstrap.Tooltip, {
28143     /**
28144      * @function init initialize tooltip monitoring.
28145      * @static
28146      */
28147     currentEl : false,
28148     currentTip : false,
28149     currentRegion : false,
28150     
28151     //  init : delay?
28152     
28153     init : function()
28154     {
28155         Roo.get(document).on('mouseover', this.enter ,this);
28156         Roo.get(document).on('mouseout', this.leave, this);
28157          
28158         
28159         this.currentTip = new Roo.bootstrap.Tooltip();
28160     },
28161     
28162     enter : function(ev)
28163     {
28164         var dom = ev.getTarget();
28165         
28166         //Roo.log(['enter',dom]);
28167         var el = Roo.fly(dom);
28168         if (this.currentEl) {
28169             //Roo.log(dom);
28170             //Roo.log(this.currentEl);
28171             //Roo.log(this.currentEl.contains(dom));
28172             if (this.currentEl == el) {
28173                 return;
28174             }
28175             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28176                 return;
28177             }
28178
28179         }
28180         
28181         if (this.currentTip.el) {
28182             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28183         }    
28184         //Roo.log(ev);
28185         
28186         if(!el || el.dom == document){
28187             return;
28188         }
28189         
28190         var bindEl = el;
28191         
28192         // you can not look for children, as if el is the body.. then everythign is the child..
28193         if (!el.attr('tooltip')) { //
28194             if (!el.select("[tooltip]").elements.length) {
28195                 return;
28196             }
28197             // is the mouse over this child...?
28198             bindEl = el.select("[tooltip]").first();
28199             var xy = ev.getXY();
28200             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28201                 //Roo.log("not in region.");
28202                 return;
28203             }
28204             //Roo.log("child element over..");
28205             
28206         }
28207         this.currentEl = bindEl;
28208         this.currentTip.bind(bindEl);
28209         this.currentRegion = Roo.lib.Region.getRegion(dom);
28210         this.currentTip.enter();
28211         
28212     },
28213     leave : function(ev)
28214     {
28215         var dom = ev.getTarget();
28216         //Roo.log(['leave',dom]);
28217         if (!this.currentEl) {
28218             return;
28219         }
28220         
28221         
28222         if (dom != this.currentEl.dom) {
28223             return;
28224         }
28225         var xy = ev.getXY();
28226         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28227             return;
28228         }
28229         // only activate leave if mouse cursor is outside... bounding box..
28230         
28231         
28232         
28233         
28234         if (this.currentTip) {
28235             this.currentTip.leave();
28236         }
28237         //Roo.log('clear currentEl');
28238         this.currentEl = false;
28239         
28240         
28241     },
28242     alignment : {
28243         'left' : ['r-l', [-2,0], 'right'],
28244         'right' : ['l-r', [2,0], 'left'],
28245         'bottom' : ['t-b', [0,2], 'top'],
28246         'top' : [ 'b-t', [0,-2], 'bottom']
28247     }
28248     
28249 });
28250
28251
28252 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28253     
28254     
28255     bindEl : false,
28256     
28257     delay : null, // can be { show : 300 , hide: 500}
28258     
28259     timeout : null,
28260     
28261     hoverState : null, //???
28262     
28263     placement : 'bottom', 
28264     
28265     alignment : false,
28266     
28267     getAutoCreate : function(){
28268     
28269         var cfg = {
28270            cls : 'tooltip',   
28271            role : 'tooltip',
28272            cn : [
28273                 {
28274                     cls : 'tooltip-arrow arrow'
28275                 },
28276                 {
28277                     cls : 'tooltip-inner'
28278                 }
28279            ]
28280         };
28281         
28282         return cfg;
28283     },
28284     bind : function(el)
28285     {
28286         this.bindEl = el;
28287     },
28288     
28289     initEvents : function()
28290     {
28291         this.arrowEl = this.el.select('.arrow', true).first();
28292     }
28293     
28294     enter : function () {
28295        
28296         if (this.timeout != null) {
28297             clearTimeout(this.timeout);
28298         }
28299         
28300         this.hoverState = 'in';
28301          //Roo.log("enter - show");
28302         if (!this.delay || !this.delay.show) {
28303             this.show();
28304             return;
28305         }
28306         var _t = this;
28307         this.timeout = setTimeout(function () {
28308             if (_t.hoverState == 'in') {
28309                 _t.show();
28310             }
28311         }, this.delay.show);
28312     },
28313     leave : function()
28314     {
28315         clearTimeout(this.timeout);
28316     
28317         this.hoverState = 'out';
28318          if (!this.delay || !this.delay.hide) {
28319             this.hide();
28320             return;
28321         }
28322        
28323         var _t = this;
28324         this.timeout = setTimeout(function () {
28325             //Roo.log("leave - timeout");
28326             
28327             if (_t.hoverState == 'out') {
28328                 _t.hide();
28329                 Roo.bootstrap.Tooltip.currentEl = false;
28330             }
28331         }, delay);
28332     },
28333     
28334     show : function (msg)
28335     {
28336         if (!this.el) {
28337             this.render(document.body);
28338         }
28339         // set content.
28340         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28341         
28342         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28343         
28344         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28345         
28346         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28347                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28348         
28349         var placement = typeof this.placement == 'function' ?
28350             this.placement.call(this, this.el, on_el) :
28351             this.placement;
28352             
28353         var autoToken = /\s?auto?\s?/i;
28354         var autoPlace = autoToken.test(placement);
28355         if (autoPlace) {
28356             placement = placement.replace(autoToken, '') || 'top';
28357         }
28358         
28359         //this.el.detach()
28360         //this.el.setXY([0,0]);
28361         this.el.show();
28362         //this.el.dom.style.display='block';
28363         
28364         //this.el.appendTo(on_el);
28365         
28366         var p = this.getPosition();
28367         var box = this.el.getBox();
28368         
28369         if (autoPlace) {
28370             // fixme..
28371         }
28372         
28373         var align = this.alignment[placement];
28374         
28375         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28376         
28377         if(placement == 'top' || placement == 'bottom'){
28378             if(xy[0] < 0){
28379                 placement = 'right';
28380             }
28381             
28382             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28383                 placement = 'left';
28384             }
28385             
28386             var scroll = Roo.select('body', true).first().getScroll();
28387             
28388             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28389                 placement = 'top';
28390             }
28391             
28392             align = this.alignment[placement];
28393         }
28394         
28395         this.el.alignTo(this.bindEl, align[0],align[1]);
28396         //var arrow = this.el.select('.arrow',true).first();
28397         //arrow.set(align[2], 
28398         
28399         this.el.addClass(placement);
28400         this.el.addClass("bs-tooltip-"+ placement);
28401         
28402         this.el.addClass('in fade show');
28403         
28404         this.hoverState = null;
28405         
28406         if (this.el.hasClass('fade')) {
28407             // fade it?
28408         }
28409         
28410         
28411         
28412         
28413         
28414     },
28415     hide : function()
28416     {
28417          
28418         if (!this.el) {
28419             return;
28420         }
28421         //this.el.setXY([0,0]);
28422         this.el.removeClass(['show', 'in']);
28423         //this.el.hide();
28424         
28425     }
28426     
28427 });
28428  
28429
28430  /*
28431  * - LGPL
28432  *
28433  * Location Picker
28434  * 
28435  */
28436
28437 /**
28438  * @class Roo.bootstrap.LocationPicker
28439  * @extends Roo.bootstrap.Component
28440  * Bootstrap LocationPicker class
28441  * @cfg {Number} latitude Position when init default 0
28442  * @cfg {Number} longitude Position when init default 0
28443  * @cfg {Number} zoom default 15
28444  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28445  * @cfg {Boolean} mapTypeControl default false
28446  * @cfg {Boolean} disableDoubleClickZoom default false
28447  * @cfg {Boolean} scrollwheel default true
28448  * @cfg {Boolean} streetViewControl default false
28449  * @cfg {Number} radius default 0
28450  * @cfg {String} locationName
28451  * @cfg {Boolean} draggable default true
28452  * @cfg {Boolean} enableAutocomplete default false
28453  * @cfg {Boolean} enableReverseGeocode default true
28454  * @cfg {String} markerTitle
28455  * 
28456  * @constructor
28457  * Create a new LocationPicker
28458  * @param {Object} config The config object
28459  */
28460
28461
28462 Roo.bootstrap.LocationPicker = function(config){
28463     
28464     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28465     
28466     this.addEvents({
28467         /**
28468          * @event initial
28469          * Fires when the picker initialized.
28470          * @param {Roo.bootstrap.LocationPicker} this
28471          * @param {Google Location} location
28472          */
28473         initial : true,
28474         /**
28475          * @event positionchanged
28476          * Fires when the picker position changed.
28477          * @param {Roo.bootstrap.LocationPicker} this
28478          * @param {Google Location} location
28479          */
28480         positionchanged : true,
28481         /**
28482          * @event resize
28483          * Fires when the map resize.
28484          * @param {Roo.bootstrap.LocationPicker} this
28485          */
28486         resize : true,
28487         /**
28488          * @event show
28489          * Fires when the map show.
28490          * @param {Roo.bootstrap.LocationPicker} this
28491          */
28492         show : true,
28493         /**
28494          * @event hide
28495          * Fires when the map hide.
28496          * @param {Roo.bootstrap.LocationPicker} this
28497          */
28498         hide : true,
28499         /**
28500          * @event mapClick
28501          * Fires when click the map.
28502          * @param {Roo.bootstrap.LocationPicker} this
28503          * @param {Map event} e
28504          */
28505         mapClick : true,
28506         /**
28507          * @event mapRightClick
28508          * Fires when right click the map.
28509          * @param {Roo.bootstrap.LocationPicker} this
28510          * @param {Map event} e
28511          */
28512         mapRightClick : true,
28513         /**
28514          * @event markerClick
28515          * Fires when click the marker.
28516          * @param {Roo.bootstrap.LocationPicker} this
28517          * @param {Map event} e
28518          */
28519         markerClick : true,
28520         /**
28521          * @event markerRightClick
28522          * Fires when right click the marker.
28523          * @param {Roo.bootstrap.LocationPicker} this
28524          * @param {Map event} e
28525          */
28526         markerRightClick : true,
28527         /**
28528          * @event OverlayViewDraw
28529          * Fires when OverlayView Draw
28530          * @param {Roo.bootstrap.LocationPicker} this
28531          */
28532         OverlayViewDraw : true,
28533         /**
28534          * @event OverlayViewOnAdd
28535          * Fires when OverlayView Draw
28536          * @param {Roo.bootstrap.LocationPicker} this
28537          */
28538         OverlayViewOnAdd : true,
28539         /**
28540          * @event OverlayViewOnRemove
28541          * Fires when OverlayView Draw
28542          * @param {Roo.bootstrap.LocationPicker} this
28543          */
28544         OverlayViewOnRemove : true,
28545         /**
28546          * @event OverlayViewShow
28547          * Fires when OverlayView Draw
28548          * @param {Roo.bootstrap.LocationPicker} this
28549          * @param {Pixel} cpx
28550          */
28551         OverlayViewShow : true,
28552         /**
28553          * @event OverlayViewHide
28554          * Fires when OverlayView Draw
28555          * @param {Roo.bootstrap.LocationPicker} this
28556          */
28557         OverlayViewHide : true,
28558         /**
28559          * @event loadexception
28560          * Fires when load google lib failed.
28561          * @param {Roo.bootstrap.LocationPicker} this
28562          */
28563         loadexception : true
28564     });
28565         
28566 };
28567
28568 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28569     
28570     gMapContext: false,
28571     
28572     latitude: 0,
28573     longitude: 0,
28574     zoom: 15,
28575     mapTypeId: false,
28576     mapTypeControl: false,
28577     disableDoubleClickZoom: false,
28578     scrollwheel: true,
28579     streetViewControl: false,
28580     radius: 0,
28581     locationName: '',
28582     draggable: true,
28583     enableAutocomplete: false,
28584     enableReverseGeocode: true,
28585     markerTitle: '',
28586     
28587     getAutoCreate: function()
28588     {
28589
28590         var cfg = {
28591             tag: 'div',
28592             cls: 'roo-location-picker'
28593         };
28594         
28595         return cfg
28596     },
28597     
28598     initEvents: function(ct, position)
28599     {       
28600         if(!this.el.getWidth() || this.isApplied()){
28601             return;
28602         }
28603         
28604         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28605         
28606         this.initial();
28607     },
28608     
28609     initial: function()
28610     {
28611         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28612             this.fireEvent('loadexception', this);
28613             return;
28614         }
28615         
28616         if(!this.mapTypeId){
28617             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28618         }
28619         
28620         this.gMapContext = this.GMapContext();
28621         
28622         this.initOverlayView();
28623         
28624         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28625         
28626         var _this = this;
28627                 
28628         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28629             _this.setPosition(_this.gMapContext.marker.position);
28630         });
28631         
28632         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28633             _this.fireEvent('mapClick', this, event);
28634             
28635         });
28636
28637         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28638             _this.fireEvent('mapRightClick', this, event);
28639             
28640         });
28641         
28642         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28643             _this.fireEvent('markerClick', this, event);
28644             
28645         });
28646
28647         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28648             _this.fireEvent('markerRightClick', this, event);
28649             
28650         });
28651         
28652         this.setPosition(this.gMapContext.location);
28653         
28654         this.fireEvent('initial', this, this.gMapContext.location);
28655     },
28656     
28657     initOverlayView: function()
28658     {
28659         var _this = this;
28660         
28661         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28662             
28663             draw: function()
28664             {
28665                 _this.fireEvent('OverlayViewDraw', _this);
28666             },
28667             
28668             onAdd: function()
28669             {
28670                 _this.fireEvent('OverlayViewOnAdd', _this);
28671             },
28672             
28673             onRemove: function()
28674             {
28675                 _this.fireEvent('OverlayViewOnRemove', _this);
28676             },
28677             
28678             show: function(cpx)
28679             {
28680                 _this.fireEvent('OverlayViewShow', _this, cpx);
28681             },
28682             
28683             hide: function()
28684             {
28685                 _this.fireEvent('OverlayViewHide', _this);
28686             }
28687             
28688         });
28689     },
28690     
28691     fromLatLngToContainerPixel: function(event)
28692     {
28693         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28694     },
28695     
28696     isApplied: function() 
28697     {
28698         return this.getGmapContext() == false ? false : true;
28699     },
28700     
28701     getGmapContext: function() 
28702     {
28703         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28704     },
28705     
28706     GMapContext: function() 
28707     {
28708         var position = new google.maps.LatLng(this.latitude, this.longitude);
28709         
28710         var _map = new google.maps.Map(this.el.dom, {
28711             center: position,
28712             zoom: this.zoom,
28713             mapTypeId: this.mapTypeId,
28714             mapTypeControl: this.mapTypeControl,
28715             disableDoubleClickZoom: this.disableDoubleClickZoom,
28716             scrollwheel: this.scrollwheel,
28717             streetViewControl: this.streetViewControl,
28718             locationName: this.locationName,
28719             draggable: this.draggable,
28720             enableAutocomplete: this.enableAutocomplete,
28721             enableReverseGeocode: this.enableReverseGeocode
28722         });
28723         
28724         var _marker = new google.maps.Marker({
28725             position: position,
28726             map: _map,
28727             title: this.markerTitle,
28728             draggable: this.draggable
28729         });
28730         
28731         return {
28732             map: _map,
28733             marker: _marker,
28734             circle: null,
28735             location: position,
28736             radius: this.radius,
28737             locationName: this.locationName,
28738             addressComponents: {
28739                 formatted_address: null,
28740                 addressLine1: null,
28741                 addressLine2: null,
28742                 streetName: null,
28743                 streetNumber: null,
28744                 city: null,
28745                 district: null,
28746                 state: null,
28747                 stateOrProvince: null
28748             },
28749             settings: this,
28750             domContainer: this.el.dom,
28751             geodecoder: new google.maps.Geocoder()
28752         };
28753     },
28754     
28755     drawCircle: function(center, radius, options) 
28756     {
28757         if (this.gMapContext.circle != null) {
28758             this.gMapContext.circle.setMap(null);
28759         }
28760         if (radius > 0) {
28761             radius *= 1;
28762             options = Roo.apply({}, options, {
28763                 strokeColor: "#0000FF",
28764                 strokeOpacity: .35,
28765                 strokeWeight: 2,
28766                 fillColor: "#0000FF",
28767                 fillOpacity: .2
28768             });
28769             
28770             options.map = this.gMapContext.map;
28771             options.radius = radius;
28772             options.center = center;
28773             this.gMapContext.circle = new google.maps.Circle(options);
28774             return this.gMapContext.circle;
28775         }
28776         
28777         return null;
28778     },
28779     
28780     setPosition: function(location) 
28781     {
28782         this.gMapContext.location = location;
28783         this.gMapContext.marker.setPosition(location);
28784         this.gMapContext.map.panTo(location);
28785         this.drawCircle(location, this.gMapContext.radius, {});
28786         
28787         var _this = this;
28788         
28789         if (this.gMapContext.settings.enableReverseGeocode) {
28790             this.gMapContext.geodecoder.geocode({
28791                 latLng: this.gMapContext.location
28792             }, function(results, status) {
28793                 
28794                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28795                     _this.gMapContext.locationName = results[0].formatted_address;
28796                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28797                     
28798                     _this.fireEvent('positionchanged', this, location);
28799                 }
28800             });
28801             
28802             return;
28803         }
28804         
28805         this.fireEvent('positionchanged', this, location);
28806     },
28807     
28808     resize: function()
28809     {
28810         google.maps.event.trigger(this.gMapContext.map, "resize");
28811         
28812         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28813         
28814         this.fireEvent('resize', this);
28815     },
28816     
28817     setPositionByLatLng: function(latitude, longitude)
28818     {
28819         this.setPosition(new google.maps.LatLng(latitude, longitude));
28820     },
28821     
28822     getCurrentPosition: function() 
28823     {
28824         return {
28825             latitude: this.gMapContext.location.lat(),
28826             longitude: this.gMapContext.location.lng()
28827         };
28828     },
28829     
28830     getAddressName: function() 
28831     {
28832         return this.gMapContext.locationName;
28833     },
28834     
28835     getAddressComponents: function() 
28836     {
28837         return this.gMapContext.addressComponents;
28838     },
28839     
28840     address_component_from_google_geocode: function(address_components) 
28841     {
28842         var result = {};
28843         
28844         for (var i = 0; i < address_components.length; i++) {
28845             var component = address_components[i];
28846             if (component.types.indexOf("postal_code") >= 0) {
28847                 result.postalCode = component.short_name;
28848             } else if (component.types.indexOf("street_number") >= 0) {
28849                 result.streetNumber = component.short_name;
28850             } else if (component.types.indexOf("route") >= 0) {
28851                 result.streetName = component.short_name;
28852             } else if (component.types.indexOf("neighborhood") >= 0) {
28853                 result.city = component.short_name;
28854             } else if (component.types.indexOf("locality") >= 0) {
28855                 result.city = component.short_name;
28856             } else if (component.types.indexOf("sublocality") >= 0) {
28857                 result.district = component.short_name;
28858             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
28859                 result.stateOrProvince = component.short_name;
28860             } else if (component.types.indexOf("country") >= 0) {
28861                 result.country = component.short_name;
28862             }
28863         }
28864         
28865         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
28866         result.addressLine2 = "";
28867         return result;
28868     },
28869     
28870     setZoomLevel: function(zoom)
28871     {
28872         this.gMapContext.map.setZoom(zoom);
28873     },
28874     
28875     show: function()
28876     {
28877         if(!this.el){
28878             return;
28879         }
28880         
28881         this.el.show();
28882         
28883         this.resize();
28884         
28885         this.fireEvent('show', this);
28886     },
28887     
28888     hide: function()
28889     {
28890         if(!this.el){
28891             return;
28892         }
28893         
28894         this.el.hide();
28895         
28896         this.fireEvent('hide', this);
28897     }
28898     
28899 });
28900
28901 Roo.apply(Roo.bootstrap.LocationPicker, {
28902     
28903     OverlayView : function(map, options)
28904     {
28905         options = options || {};
28906         
28907         this.setMap(map);
28908     }
28909     
28910     
28911 });/**
28912  * @class Roo.bootstrap.Alert
28913  * @extends Roo.bootstrap.Component
28914  * Bootstrap Alert class - shows an alert area box
28915  * eg
28916  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
28917   Enter a valid email address
28918 </div>
28919  * @licence LGPL
28920  * @cfg {String} title The title of alert
28921  * @cfg {String} html The content of alert
28922  * @cfg {String} weight (  success | info | warning | danger )
28923  * @cfg {String} faicon font-awesomeicon
28924  * 
28925  * @constructor
28926  * Create a new alert
28927  * @param {Object} config The config object
28928  */
28929
28930
28931 Roo.bootstrap.Alert = function(config){
28932     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
28933     
28934 };
28935
28936 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
28937     
28938     title: '',
28939     html: '',
28940     weight: false,
28941     faicon: false,
28942     
28943     getAutoCreate : function()
28944     {
28945         
28946         var cfg = {
28947             tag : 'div',
28948             cls : 'alert',
28949             cn : [
28950                 {
28951                     tag : 'i',
28952                     cls : 'roo-alert-icon'
28953                     
28954                 },
28955                 {
28956                     tag : 'b',
28957                     cls : 'roo-alert-title',
28958                     html : this.title
28959                 },
28960                 {
28961                     tag : 'span',
28962                     cls : 'roo-alert-text',
28963                     html : this.html
28964                 }
28965             ]
28966         };
28967         
28968         if(this.faicon){
28969             cfg.cn[0].cls += ' fa ' + this.faicon;
28970         }
28971         
28972         if(this.weight){
28973             cfg.cls += ' alert-' + this.weight;
28974         }
28975         
28976         return cfg;
28977     },
28978     
28979     initEvents: function() 
28980     {
28981         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28982     },
28983     
28984     setTitle : function(str)
28985     {
28986         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
28987     },
28988     
28989     setText : function(str)
28990     {
28991         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
28992     },
28993     
28994     setWeight : function(weight)
28995     {
28996         if(this.weight){
28997             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
28998         }
28999         
29000         this.weight = weight;
29001         
29002         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29003     },
29004     
29005     setIcon : function(icon)
29006     {
29007         if(this.faicon){
29008             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29009         }
29010         
29011         this.faicon = icon;
29012         
29013         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29014     },
29015     
29016     hide: function() 
29017     {
29018         this.el.hide();   
29019     },
29020     
29021     show: function() 
29022     {  
29023         this.el.show();   
29024     }
29025     
29026 });
29027
29028  
29029 /*
29030 * Licence: LGPL
29031 */
29032
29033 /**
29034  * @class Roo.bootstrap.UploadCropbox
29035  * @extends Roo.bootstrap.Component
29036  * Bootstrap UploadCropbox class
29037  * @cfg {String} emptyText show when image has been loaded
29038  * @cfg {String} rotateNotify show when image too small to rotate
29039  * @cfg {Number} errorTimeout default 3000
29040  * @cfg {Number} minWidth default 300
29041  * @cfg {Number} minHeight default 300
29042  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29043  * @cfg {Boolean} isDocument (true|false) default false
29044  * @cfg {String} url action url
29045  * @cfg {String} paramName default 'imageUpload'
29046  * @cfg {String} method default POST
29047  * @cfg {Boolean} loadMask (true|false) default true
29048  * @cfg {Boolean} loadingText default 'Loading...'
29049  * 
29050  * @constructor
29051  * Create a new UploadCropbox
29052  * @param {Object} config The config object
29053  */
29054
29055 Roo.bootstrap.UploadCropbox = function(config){
29056     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29057     
29058     this.addEvents({
29059         /**
29060          * @event beforeselectfile
29061          * Fire before select file
29062          * @param {Roo.bootstrap.UploadCropbox} this
29063          */
29064         "beforeselectfile" : true,
29065         /**
29066          * @event initial
29067          * Fire after initEvent
29068          * @param {Roo.bootstrap.UploadCropbox} this
29069          */
29070         "initial" : true,
29071         /**
29072          * @event crop
29073          * Fire after initEvent
29074          * @param {Roo.bootstrap.UploadCropbox} this
29075          * @param {String} data
29076          */
29077         "crop" : true,
29078         /**
29079          * @event prepare
29080          * Fire when preparing the file data
29081          * @param {Roo.bootstrap.UploadCropbox} this
29082          * @param {Object} file
29083          */
29084         "prepare" : true,
29085         /**
29086          * @event exception
29087          * Fire when get exception
29088          * @param {Roo.bootstrap.UploadCropbox} this
29089          * @param {XMLHttpRequest} xhr
29090          */
29091         "exception" : true,
29092         /**
29093          * @event beforeloadcanvas
29094          * Fire before load the canvas
29095          * @param {Roo.bootstrap.UploadCropbox} this
29096          * @param {String} src
29097          */
29098         "beforeloadcanvas" : true,
29099         /**
29100          * @event trash
29101          * Fire when trash image
29102          * @param {Roo.bootstrap.UploadCropbox} this
29103          */
29104         "trash" : true,
29105         /**
29106          * @event download
29107          * Fire when download the image
29108          * @param {Roo.bootstrap.UploadCropbox} this
29109          */
29110         "download" : true,
29111         /**
29112          * @event footerbuttonclick
29113          * Fire when footerbuttonclick
29114          * @param {Roo.bootstrap.UploadCropbox} this
29115          * @param {String} type
29116          */
29117         "footerbuttonclick" : true,
29118         /**
29119          * @event resize
29120          * Fire when resize
29121          * @param {Roo.bootstrap.UploadCropbox} this
29122          */
29123         "resize" : true,
29124         /**
29125          * @event rotate
29126          * Fire when rotate the image
29127          * @param {Roo.bootstrap.UploadCropbox} this
29128          * @param {String} pos
29129          */
29130         "rotate" : true,
29131         /**
29132          * @event inspect
29133          * Fire when inspect the file
29134          * @param {Roo.bootstrap.UploadCropbox} this
29135          * @param {Object} file
29136          */
29137         "inspect" : true,
29138         /**
29139          * @event upload
29140          * Fire when xhr upload the file
29141          * @param {Roo.bootstrap.UploadCropbox} this
29142          * @param {Object} data
29143          */
29144         "upload" : true,
29145         /**
29146          * @event arrange
29147          * Fire when arrange the file data
29148          * @param {Roo.bootstrap.UploadCropbox} this
29149          * @param {Object} formData
29150          */
29151         "arrange" : true
29152     });
29153     
29154     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29155 };
29156
29157 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29158     
29159     emptyText : 'Click to upload image',
29160     rotateNotify : 'Image is too small to rotate',
29161     errorTimeout : 3000,
29162     scale : 0,
29163     baseScale : 1,
29164     rotate : 0,
29165     dragable : false,
29166     pinching : false,
29167     mouseX : 0,
29168     mouseY : 0,
29169     cropData : false,
29170     minWidth : 300,
29171     minHeight : 300,
29172     file : false,
29173     exif : {},
29174     baseRotate : 1,
29175     cropType : 'image/jpeg',
29176     buttons : false,
29177     canvasLoaded : false,
29178     isDocument : false,
29179     method : 'POST',
29180     paramName : 'imageUpload',
29181     loadMask : true,
29182     loadingText : 'Loading...',
29183     maskEl : false,
29184     
29185     getAutoCreate : function()
29186     {
29187         var cfg = {
29188             tag : 'div',
29189             cls : 'roo-upload-cropbox',
29190             cn : [
29191                 {
29192                     tag : 'input',
29193                     cls : 'roo-upload-cropbox-selector',
29194                     type : 'file'
29195                 },
29196                 {
29197                     tag : 'div',
29198                     cls : 'roo-upload-cropbox-body',
29199                     style : 'cursor:pointer',
29200                     cn : [
29201                         {
29202                             tag : 'div',
29203                             cls : 'roo-upload-cropbox-preview'
29204                         },
29205                         {
29206                             tag : 'div',
29207                             cls : 'roo-upload-cropbox-thumb'
29208                         },
29209                         {
29210                             tag : 'div',
29211                             cls : 'roo-upload-cropbox-empty-notify',
29212                             html : this.emptyText
29213                         },
29214                         {
29215                             tag : 'div',
29216                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29217                             html : this.rotateNotify
29218                         }
29219                     ]
29220                 },
29221                 {
29222                     tag : 'div',
29223                     cls : 'roo-upload-cropbox-footer',
29224                     cn : {
29225                         tag : 'div',
29226                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29227                         cn : []
29228                     }
29229                 }
29230             ]
29231         };
29232         
29233         return cfg;
29234     },
29235     
29236     onRender : function(ct, position)
29237     {
29238         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29239         
29240         if (this.buttons.length) {
29241             
29242             Roo.each(this.buttons, function(bb) {
29243                 
29244                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29245                 
29246                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29247                 
29248             }, this);
29249         }
29250         
29251         if(this.loadMask){
29252             this.maskEl = this.el;
29253         }
29254     },
29255     
29256     initEvents : function()
29257     {
29258         this.urlAPI = (window.createObjectURL && window) || 
29259                                 (window.URL && URL.revokeObjectURL && URL) || 
29260                                 (window.webkitURL && webkitURL);
29261                         
29262         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29263         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29264         
29265         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29266         this.selectorEl.hide();
29267         
29268         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29269         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29270         
29271         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29272         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29273         this.thumbEl.hide();
29274         
29275         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29276         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29277         
29278         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29279         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29280         this.errorEl.hide();
29281         
29282         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29283         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29284         this.footerEl.hide();
29285         
29286         this.setThumbBoxSize();
29287         
29288         this.bind();
29289         
29290         this.resize();
29291         
29292         this.fireEvent('initial', this);
29293     },
29294
29295     bind : function()
29296     {
29297         var _this = this;
29298         
29299         window.addEventListener("resize", function() { _this.resize(); } );
29300         
29301         this.bodyEl.on('click', this.beforeSelectFile, this);
29302         
29303         if(Roo.isTouch){
29304             this.bodyEl.on('touchstart', this.onTouchStart, this);
29305             this.bodyEl.on('touchmove', this.onTouchMove, this);
29306             this.bodyEl.on('touchend', this.onTouchEnd, this);
29307         }
29308         
29309         if(!Roo.isTouch){
29310             this.bodyEl.on('mousedown', this.onMouseDown, this);
29311             this.bodyEl.on('mousemove', this.onMouseMove, this);
29312             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29313             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29314             Roo.get(document).on('mouseup', this.onMouseUp, this);
29315         }
29316         
29317         this.selectorEl.on('change', this.onFileSelected, this);
29318     },
29319     
29320     reset : function()
29321     {    
29322         this.scale = 0;
29323         this.baseScale = 1;
29324         this.rotate = 0;
29325         this.baseRotate = 1;
29326         this.dragable = false;
29327         this.pinching = false;
29328         this.mouseX = 0;
29329         this.mouseY = 0;
29330         this.cropData = false;
29331         this.notifyEl.dom.innerHTML = this.emptyText;
29332         
29333         this.selectorEl.dom.value = '';
29334         
29335     },
29336     
29337     resize : function()
29338     {
29339         if(this.fireEvent('resize', this) != false){
29340             this.setThumbBoxPosition();
29341             this.setCanvasPosition();
29342         }
29343     },
29344     
29345     onFooterButtonClick : function(e, el, o, type)
29346     {
29347         switch (type) {
29348             case 'rotate-left' :
29349                 this.onRotateLeft(e);
29350                 break;
29351             case 'rotate-right' :
29352                 this.onRotateRight(e);
29353                 break;
29354             case 'picture' :
29355                 this.beforeSelectFile(e);
29356                 break;
29357             case 'trash' :
29358                 this.trash(e);
29359                 break;
29360             case 'crop' :
29361                 this.crop(e);
29362                 break;
29363             case 'download' :
29364                 this.download(e);
29365                 break;
29366             default :
29367                 break;
29368         }
29369         
29370         this.fireEvent('footerbuttonclick', this, type);
29371     },
29372     
29373     beforeSelectFile : function(e)
29374     {
29375         e.preventDefault();
29376         
29377         if(this.fireEvent('beforeselectfile', this) != false){
29378             this.selectorEl.dom.click();
29379         }
29380     },
29381     
29382     onFileSelected : function(e)
29383     {
29384         e.preventDefault();
29385         
29386         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29387             return;
29388         }
29389         
29390         var file = this.selectorEl.dom.files[0];
29391         
29392         if(this.fireEvent('inspect', this, file) != false){
29393             this.prepare(file);
29394         }
29395         
29396     },
29397     
29398     trash : function(e)
29399     {
29400         this.fireEvent('trash', this);
29401     },
29402     
29403     download : function(e)
29404     {
29405         this.fireEvent('download', this);
29406     },
29407     
29408     loadCanvas : function(src)
29409     {   
29410         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29411             
29412             this.reset();
29413             
29414             this.imageEl = document.createElement('img');
29415             
29416             var _this = this;
29417             
29418             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29419             
29420             this.imageEl.src = src;
29421         }
29422     },
29423     
29424     onLoadCanvas : function()
29425     {   
29426         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29427         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29428         
29429         this.bodyEl.un('click', this.beforeSelectFile, this);
29430         
29431         this.notifyEl.hide();
29432         this.thumbEl.show();
29433         this.footerEl.show();
29434         
29435         this.baseRotateLevel();
29436         
29437         if(this.isDocument){
29438             this.setThumbBoxSize();
29439         }
29440         
29441         this.setThumbBoxPosition();
29442         
29443         this.baseScaleLevel();
29444         
29445         this.draw();
29446         
29447         this.resize();
29448         
29449         this.canvasLoaded = true;
29450         
29451         if(this.loadMask){
29452             this.maskEl.unmask();
29453         }
29454         
29455     },
29456     
29457     setCanvasPosition : function()
29458     {   
29459         if(!this.canvasEl){
29460             return;
29461         }
29462         
29463         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29464         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29465         
29466         this.previewEl.setLeft(pw);
29467         this.previewEl.setTop(ph);
29468         
29469     },
29470     
29471     onMouseDown : function(e)
29472     {   
29473         e.stopEvent();
29474         
29475         this.dragable = true;
29476         this.pinching = false;
29477         
29478         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29479             this.dragable = false;
29480             return;
29481         }
29482         
29483         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29484         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29485         
29486     },
29487     
29488     onMouseMove : function(e)
29489     {   
29490         e.stopEvent();
29491         
29492         if(!this.canvasLoaded){
29493             return;
29494         }
29495         
29496         if (!this.dragable){
29497             return;
29498         }
29499         
29500         var minX = Math.ceil(this.thumbEl.getLeft(true));
29501         var minY = Math.ceil(this.thumbEl.getTop(true));
29502         
29503         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29504         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29505         
29506         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29507         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29508         
29509         x = x - this.mouseX;
29510         y = y - this.mouseY;
29511         
29512         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29513         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29514         
29515         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29516         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29517         
29518         this.previewEl.setLeft(bgX);
29519         this.previewEl.setTop(bgY);
29520         
29521         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29522         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29523     },
29524     
29525     onMouseUp : function(e)
29526     {   
29527         e.stopEvent();
29528         
29529         this.dragable = false;
29530     },
29531     
29532     onMouseWheel : function(e)
29533     {   
29534         e.stopEvent();
29535         
29536         this.startScale = this.scale;
29537         
29538         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29539         
29540         if(!this.zoomable()){
29541             this.scale = this.startScale;
29542             return;
29543         }
29544         
29545         this.draw();
29546         
29547         return;
29548     },
29549     
29550     zoomable : function()
29551     {
29552         var minScale = this.thumbEl.getWidth() / this.minWidth;
29553         
29554         if(this.minWidth < this.minHeight){
29555             minScale = this.thumbEl.getHeight() / this.minHeight;
29556         }
29557         
29558         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29559         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29560         
29561         if(
29562                 this.isDocument &&
29563                 (this.rotate == 0 || this.rotate == 180) && 
29564                 (
29565                     width > this.imageEl.OriginWidth || 
29566                     height > this.imageEl.OriginHeight ||
29567                     (width < this.minWidth && height < this.minHeight)
29568                 )
29569         ){
29570             return false;
29571         }
29572         
29573         if(
29574                 this.isDocument &&
29575                 (this.rotate == 90 || this.rotate == 270) && 
29576                 (
29577                     width > this.imageEl.OriginWidth || 
29578                     height > this.imageEl.OriginHeight ||
29579                     (width < this.minHeight && height < this.minWidth)
29580                 )
29581         ){
29582             return false;
29583         }
29584         
29585         if(
29586                 !this.isDocument &&
29587                 (this.rotate == 0 || this.rotate == 180) && 
29588                 (
29589                     width < this.minWidth || 
29590                     width > this.imageEl.OriginWidth || 
29591                     height < this.minHeight || 
29592                     height > this.imageEl.OriginHeight
29593                 )
29594         ){
29595             return false;
29596         }
29597         
29598         if(
29599                 !this.isDocument &&
29600                 (this.rotate == 90 || this.rotate == 270) && 
29601                 (
29602                     width < this.minHeight || 
29603                     width > this.imageEl.OriginWidth || 
29604                     height < this.minWidth || 
29605                     height > this.imageEl.OriginHeight
29606                 )
29607         ){
29608             return false;
29609         }
29610         
29611         return true;
29612         
29613     },
29614     
29615     onRotateLeft : function(e)
29616     {   
29617         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29618             
29619             var minScale = this.thumbEl.getWidth() / this.minWidth;
29620             
29621             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29622             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29623             
29624             this.startScale = this.scale;
29625             
29626             while (this.getScaleLevel() < minScale){
29627             
29628                 this.scale = this.scale + 1;
29629                 
29630                 if(!this.zoomable()){
29631                     break;
29632                 }
29633                 
29634                 if(
29635                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29636                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29637                 ){
29638                     continue;
29639                 }
29640                 
29641                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29642
29643                 this.draw();
29644                 
29645                 return;
29646             }
29647             
29648             this.scale = this.startScale;
29649             
29650             this.onRotateFail();
29651             
29652             return false;
29653         }
29654         
29655         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29656
29657         if(this.isDocument){
29658             this.setThumbBoxSize();
29659             this.setThumbBoxPosition();
29660             this.setCanvasPosition();
29661         }
29662         
29663         this.draw();
29664         
29665         this.fireEvent('rotate', this, 'left');
29666         
29667     },
29668     
29669     onRotateRight : function(e)
29670     {
29671         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29672             
29673             var minScale = this.thumbEl.getWidth() / this.minWidth;
29674         
29675             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29676             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29677             
29678             this.startScale = this.scale;
29679             
29680             while (this.getScaleLevel() < minScale){
29681             
29682                 this.scale = this.scale + 1;
29683                 
29684                 if(!this.zoomable()){
29685                     break;
29686                 }
29687                 
29688                 if(
29689                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29690                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29691                 ){
29692                     continue;
29693                 }
29694                 
29695                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29696
29697                 this.draw();
29698                 
29699                 return;
29700             }
29701             
29702             this.scale = this.startScale;
29703             
29704             this.onRotateFail();
29705             
29706             return false;
29707         }
29708         
29709         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29710
29711         if(this.isDocument){
29712             this.setThumbBoxSize();
29713             this.setThumbBoxPosition();
29714             this.setCanvasPosition();
29715         }
29716         
29717         this.draw();
29718         
29719         this.fireEvent('rotate', this, 'right');
29720     },
29721     
29722     onRotateFail : function()
29723     {
29724         this.errorEl.show(true);
29725         
29726         var _this = this;
29727         
29728         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29729     },
29730     
29731     draw : function()
29732     {
29733         this.previewEl.dom.innerHTML = '';
29734         
29735         var canvasEl = document.createElement("canvas");
29736         
29737         var contextEl = canvasEl.getContext("2d");
29738         
29739         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29740         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29741         var center = this.imageEl.OriginWidth / 2;
29742         
29743         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29744             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29745             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29746             center = this.imageEl.OriginHeight / 2;
29747         }
29748         
29749         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29750         
29751         contextEl.translate(center, center);
29752         contextEl.rotate(this.rotate * Math.PI / 180);
29753
29754         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29755         
29756         this.canvasEl = document.createElement("canvas");
29757         
29758         this.contextEl = this.canvasEl.getContext("2d");
29759         
29760         switch (this.rotate) {
29761             case 0 :
29762                 
29763                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29764                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29765                 
29766                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29767                 
29768                 break;
29769             case 90 : 
29770                 
29771                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29772                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29773                 
29774                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29775                     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);
29776                     break;
29777                 }
29778                 
29779                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29780                 
29781                 break;
29782             case 180 :
29783                 
29784                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29785                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29786                 
29787                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29788                     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);
29789                     break;
29790                 }
29791                 
29792                 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);
29793                 
29794                 break;
29795             case 270 :
29796                 
29797                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29798                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29799         
29800                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29801                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29802                     break;
29803                 }
29804                 
29805                 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);
29806                 
29807                 break;
29808             default : 
29809                 break;
29810         }
29811         
29812         this.previewEl.appendChild(this.canvasEl);
29813         
29814         this.setCanvasPosition();
29815     },
29816     
29817     crop : function()
29818     {
29819         if(!this.canvasLoaded){
29820             return;
29821         }
29822         
29823         var imageCanvas = document.createElement("canvas");
29824         
29825         var imageContext = imageCanvas.getContext("2d");
29826         
29827         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29828         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29829         
29830         var center = imageCanvas.width / 2;
29831         
29832         imageContext.translate(center, center);
29833         
29834         imageContext.rotate(this.rotate * Math.PI / 180);
29835         
29836         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29837         
29838         var canvas = document.createElement("canvas");
29839         
29840         var context = canvas.getContext("2d");
29841                 
29842         canvas.width = this.minWidth;
29843         canvas.height = this.minHeight;
29844
29845         switch (this.rotate) {
29846             case 0 :
29847                 
29848                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29849                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29850                 
29851                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29852                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29853                 
29854                 var targetWidth = this.minWidth - 2 * x;
29855                 var targetHeight = this.minHeight - 2 * y;
29856                 
29857                 var scale = 1;
29858                 
29859                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29860                     scale = targetWidth / width;
29861                 }
29862                 
29863                 if(x > 0 && y == 0){
29864                     scale = targetHeight / height;
29865                 }
29866                 
29867                 if(x > 0 && y > 0){
29868                     scale = targetWidth / width;
29869                     
29870                     if(width < height){
29871                         scale = targetHeight / height;
29872                     }
29873                 }
29874                 
29875                 context.scale(scale, scale);
29876                 
29877                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29878                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29879
29880                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29881                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29882
29883                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29884                 
29885                 break;
29886             case 90 : 
29887                 
29888                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29889                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29890                 
29891                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29892                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29893                 
29894                 var targetWidth = this.minWidth - 2 * x;
29895                 var targetHeight = this.minHeight - 2 * y;
29896                 
29897                 var scale = 1;
29898                 
29899                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29900                     scale = targetWidth / width;
29901                 }
29902                 
29903                 if(x > 0 && y == 0){
29904                     scale = targetHeight / height;
29905                 }
29906                 
29907                 if(x > 0 && y > 0){
29908                     scale = targetWidth / width;
29909                     
29910                     if(width < height){
29911                         scale = targetHeight / height;
29912                     }
29913                 }
29914                 
29915                 context.scale(scale, scale);
29916                 
29917                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29918                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29919
29920                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29921                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29922                 
29923                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29924                 
29925                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29926                 
29927                 break;
29928             case 180 :
29929                 
29930                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
29931                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
29932                 
29933                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29934                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29935                 
29936                 var targetWidth = this.minWidth - 2 * x;
29937                 var targetHeight = this.minHeight - 2 * y;
29938                 
29939                 var scale = 1;
29940                 
29941                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29942                     scale = targetWidth / width;
29943                 }
29944                 
29945                 if(x > 0 && y == 0){
29946                     scale = targetHeight / height;
29947                 }
29948                 
29949                 if(x > 0 && y > 0){
29950                     scale = targetWidth / width;
29951                     
29952                     if(width < height){
29953                         scale = targetHeight / height;
29954                     }
29955                 }
29956                 
29957                 context.scale(scale, scale);
29958                 
29959                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
29960                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
29961
29962                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
29963                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
29964
29965                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
29966                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
29967                 
29968                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
29969                 
29970                 break;
29971             case 270 :
29972                 
29973                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
29974                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
29975                 
29976                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
29977                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
29978                 
29979                 var targetWidth = this.minWidth - 2 * x;
29980                 var targetHeight = this.minHeight - 2 * y;
29981                 
29982                 var scale = 1;
29983                 
29984                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
29985                     scale = targetWidth / width;
29986                 }
29987                 
29988                 if(x > 0 && y == 0){
29989                     scale = targetHeight / height;
29990                 }
29991                 
29992                 if(x > 0 && y > 0){
29993                     scale = targetWidth / width;
29994                     
29995                     if(width < height){
29996                         scale = targetHeight / height;
29997                     }
29998                 }
29999                 
30000                 context.scale(scale, scale);
30001                 
30002                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30003                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30004
30005                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30006                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30007                 
30008                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30009                 
30010                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30011                 
30012                 break;
30013             default : 
30014                 break;
30015         }
30016         
30017         this.cropData = canvas.toDataURL(this.cropType);
30018         
30019         if(this.fireEvent('crop', this, this.cropData) !== false){
30020             this.process(this.file, this.cropData);
30021         }
30022         
30023         return;
30024         
30025     },
30026     
30027     setThumbBoxSize : function()
30028     {
30029         var width, height;
30030         
30031         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30032             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30033             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30034             
30035             this.minWidth = width;
30036             this.minHeight = height;
30037             
30038             if(this.rotate == 90 || this.rotate == 270){
30039                 this.minWidth = height;
30040                 this.minHeight = width;
30041             }
30042         }
30043         
30044         height = 300;
30045         width = Math.ceil(this.minWidth * height / this.minHeight);
30046         
30047         if(this.minWidth > this.minHeight){
30048             width = 300;
30049             height = Math.ceil(this.minHeight * width / this.minWidth);
30050         }
30051         
30052         this.thumbEl.setStyle({
30053             width : width + 'px',
30054             height : height + 'px'
30055         });
30056
30057         return;
30058             
30059     },
30060     
30061     setThumbBoxPosition : function()
30062     {
30063         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30064         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30065         
30066         this.thumbEl.setLeft(x);
30067         this.thumbEl.setTop(y);
30068         
30069     },
30070     
30071     baseRotateLevel : function()
30072     {
30073         this.baseRotate = 1;
30074         
30075         if(
30076                 typeof(this.exif) != 'undefined' &&
30077                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30078                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30079         ){
30080             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30081         }
30082         
30083         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30084         
30085     },
30086     
30087     baseScaleLevel : function()
30088     {
30089         var width, height;
30090         
30091         if(this.isDocument){
30092             
30093             if(this.baseRotate == 6 || this.baseRotate == 8){
30094             
30095                 height = this.thumbEl.getHeight();
30096                 this.baseScale = height / this.imageEl.OriginWidth;
30097
30098                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30099                     width = this.thumbEl.getWidth();
30100                     this.baseScale = width / this.imageEl.OriginHeight;
30101                 }
30102
30103                 return;
30104             }
30105
30106             height = this.thumbEl.getHeight();
30107             this.baseScale = height / this.imageEl.OriginHeight;
30108
30109             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30110                 width = this.thumbEl.getWidth();
30111                 this.baseScale = width / this.imageEl.OriginWidth;
30112             }
30113
30114             return;
30115         }
30116         
30117         if(this.baseRotate == 6 || this.baseRotate == 8){
30118             
30119             width = this.thumbEl.getHeight();
30120             this.baseScale = width / this.imageEl.OriginHeight;
30121             
30122             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30123                 height = this.thumbEl.getWidth();
30124                 this.baseScale = height / this.imageEl.OriginHeight;
30125             }
30126             
30127             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30128                 height = this.thumbEl.getWidth();
30129                 this.baseScale = height / this.imageEl.OriginHeight;
30130                 
30131                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30132                     width = this.thumbEl.getHeight();
30133                     this.baseScale = width / this.imageEl.OriginWidth;
30134                 }
30135             }
30136             
30137             return;
30138         }
30139         
30140         width = this.thumbEl.getWidth();
30141         this.baseScale = width / this.imageEl.OriginWidth;
30142         
30143         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30144             height = this.thumbEl.getHeight();
30145             this.baseScale = height / this.imageEl.OriginHeight;
30146         }
30147         
30148         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30149             
30150             height = this.thumbEl.getHeight();
30151             this.baseScale = height / this.imageEl.OriginHeight;
30152             
30153             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30154                 width = this.thumbEl.getWidth();
30155                 this.baseScale = width / this.imageEl.OriginWidth;
30156             }
30157             
30158         }
30159         
30160         return;
30161     },
30162     
30163     getScaleLevel : function()
30164     {
30165         return this.baseScale * Math.pow(1.1, this.scale);
30166     },
30167     
30168     onTouchStart : function(e)
30169     {
30170         if(!this.canvasLoaded){
30171             this.beforeSelectFile(e);
30172             return;
30173         }
30174         
30175         var touches = e.browserEvent.touches;
30176         
30177         if(!touches){
30178             return;
30179         }
30180         
30181         if(touches.length == 1){
30182             this.onMouseDown(e);
30183             return;
30184         }
30185         
30186         if(touches.length != 2){
30187             return;
30188         }
30189         
30190         var coords = [];
30191         
30192         for(var i = 0, finger; finger = touches[i]; i++){
30193             coords.push(finger.pageX, finger.pageY);
30194         }
30195         
30196         var x = Math.pow(coords[0] - coords[2], 2);
30197         var y = Math.pow(coords[1] - coords[3], 2);
30198         
30199         this.startDistance = Math.sqrt(x + y);
30200         
30201         this.startScale = this.scale;
30202         
30203         this.pinching = true;
30204         this.dragable = false;
30205         
30206     },
30207     
30208     onTouchMove : function(e)
30209     {
30210         if(!this.pinching && !this.dragable){
30211             return;
30212         }
30213         
30214         var touches = e.browserEvent.touches;
30215         
30216         if(!touches){
30217             return;
30218         }
30219         
30220         if(this.dragable){
30221             this.onMouseMove(e);
30222             return;
30223         }
30224         
30225         var coords = [];
30226         
30227         for(var i = 0, finger; finger = touches[i]; i++){
30228             coords.push(finger.pageX, finger.pageY);
30229         }
30230         
30231         var x = Math.pow(coords[0] - coords[2], 2);
30232         var y = Math.pow(coords[1] - coords[3], 2);
30233         
30234         this.endDistance = Math.sqrt(x + y);
30235         
30236         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30237         
30238         if(!this.zoomable()){
30239             this.scale = this.startScale;
30240             return;
30241         }
30242         
30243         this.draw();
30244         
30245     },
30246     
30247     onTouchEnd : function(e)
30248     {
30249         this.pinching = false;
30250         this.dragable = false;
30251         
30252     },
30253     
30254     process : function(file, crop)
30255     {
30256         if(this.loadMask){
30257             this.maskEl.mask(this.loadingText);
30258         }
30259         
30260         this.xhr = new XMLHttpRequest();
30261         
30262         file.xhr = this.xhr;
30263
30264         this.xhr.open(this.method, this.url, true);
30265         
30266         var headers = {
30267             "Accept": "application/json",
30268             "Cache-Control": "no-cache",
30269             "X-Requested-With": "XMLHttpRequest"
30270         };
30271         
30272         for (var headerName in headers) {
30273             var headerValue = headers[headerName];
30274             if (headerValue) {
30275                 this.xhr.setRequestHeader(headerName, headerValue);
30276             }
30277         }
30278         
30279         var _this = this;
30280         
30281         this.xhr.onload = function()
30282         {
30283             _this.xhrOnLoad(_this.xhr);
30284         }
30285         
30286         this.xhr.onerror = function()
30287         {
30288             _this.xhrOnError(_this.xhr);
30289         }
30290         
30291         var formData = new FormData();
30292
30293         formData.append('returnHTML', 'NO');
30294         
30295         if(crop){
30296             formData.append('crop', crop);
30297         }
30298         
30299         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30300             formData.append(this.paramName, file, file.name);
30301         }
30302         
30303         if(typeof(file.filename) != 'undefined'){
30304             formData.append('filename', file.filename);
30305         }
30306         
30307         if(typeof(file.mimetype) != 'undefined'){
30308             formData.append('mimetype', file.mimetype);
30309         }
30310         
30311         if(this.fireEvent('arrange', this, formData) != false){
30312             this.xhr.send(formData);
30313         };
30314     },
30315     
30316     xhrOnLoad : function(xhr)
30317     {
30318         if(this.loadMask){
30319             this.maskEl.unmask();
30320         }
30321         
30322         if (xhr.readyState !== 4) {
30323             this.fireEvent('exception', this, xhr);
30324             return;
30325         }
30326
30327         var response = Roo.decode(xhr.responseText);
30328         
30329         if(!response.success){
30330             this.fireEvent('exception', this, xhr);
30331             return;
30332         }
30333         
30334         var response = Roo.decode(xhr.responseText);
30335         
30336         this.fireEvent('upload', this, response);
30337         
30338     },
30339     
30340     xhrOnError : function()
30341     {
30342         if(this.loadMask){
30343             this.maskEl.unmask();
30344         }
30345         
30346         Roo.log('xhr on error');
30347         
30348         var response = Roo.decode(xhr.responseText);
30349           
30350         Roo.log(response);
30351         
30352     },
30353     
30354     prepare : function(file)
30355     {   
30356         if(this.loadMask){
30357             this.maskEl.mask(this.loadingText);
30358         }
30359         
30360         this.file = false;
30361         this.exif = {};
30362         
30363         if(typeof(file) === 'string'){
30364             this.loadCanvas(file);
30365             return;
30366         }
30367         
30368         if(!file || !this.urlAPI){
30369             return;
30370         }
30371         
30372         this.file = file;
30373         this.cropType = file.type;
30374         
30375         var _this = this;
30376         
30377         if(this.fireEvent('prepare', this, this.file) != false){
30378             
30379             var reader = new FileReader();
30380             
30381             reader.onload = function (e) {
30382                 if (e.target.error) {
30383                     Roo.log(e.target.error);
30384                     return;
30385                 }
30386                 
30387                 var buffer = e.target.result,
30388                     dataView = new DataView(buffer),
30389                     offset = 2,
30390                     maxOffset = dataView.byteLength - 4,
30391                     markerBytes,
30392                     markerLength;
30393                 
30394                 if (dataView.getUint16(0) === 0xffd8) {
30395                     while (offset < maxOffset) {
30396                         markerBytes = dataView.getUint16(offset);
30397                         
30398                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30399                             markerLength = dataView.getUint16(offset + 2) + 2;
30400                             if (offset + markerLength > dataView.byteLength) {
30401                                 Roo.log('Invalid meta data: Invalid segment size.');
30402                                 break;
30403                             }
30404                             
30405                             if(markerBytes == 0xffe1){
30406                                 _this.parseExifData(
30407                                     dataView,
30408                                     offset,
30409                                     markerLength
30410                                 );
30411                             }
30412                             
30413                             offset += markerLength;
30414                             
30415                             continue;
30416                         }
30417                         
30418                         break;
30419                     }
30420                     
30421                 }
30422                 
30423                 var url = _this.urlAPI.createObjectURL(_this.file);
30424                 
30425                 _this.loadCanvas(url);
30426                 
30427                 return;
30428             }
30429             
30430             reader.readAsArrayBuffer(this.file);
30431             
30432         }
30433         
30434     },
30435     
30436     parseExifData : function(dataView, offset, length)
30437     {
30438         var tiffOffset = offset + 10,
30439             littleEndian,
30440             dirOffset;
30441     
30442         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30443             // No Exif data, might be XMP data instead
30444             return;
30445         }
30446         
30447         // Check for the ASCII code for "Exif" (0x45786966):
30448         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30449             // No Exif data, might be XMP data instead
30450             return;
30451         }
30452         if (tiffOffset + 8 > dataView.byteLength) {
30453             Roo.log('Invalid Exif data: Invalid segment size.');
30454             return;
30455         }
30456         // Check for the two null bytes:
30457         if (dataView.getUint16(offset + 8) !== 0x0000) {
30458             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30459             return;
30460         }
30461         // Check the byte alignment:
30462         switch (dataView.getUint16(tiffOffset)) {
30463         case 0x4949:
30464             littleEndian = true;
30465             break;
30466         case 0x4D4D:
30467             littleEndian = false;
30468             break;
30469         default:
30470             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30471             return;
30472         }
30473         // Check for the TIFF tag marker (0x002A):
30474         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30475             Roo.log('Invalid Exif data: Missing TIFF marker.');
30476             return;
30477         }
30478         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30479         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30480         
30481         this.parseExifTags(
30482             dataView,
30483             tiffOffset,
30484             tiffOffset + dirOffset,
30485             littleEndian
30486         );
30487     },
30488     
30489     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30490     {
30491         var tagsNumber,
30492             dirEndOffset,
30493             i;
30494         if (dirOffset + 6 > dataView.byteLength) {
30495             Roo.log('Invalid Exif data: Invalid directory offset.');
30496             return;
30497         }
30498         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30499         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30500         if (dirEndOffset + 4 > dataView.byteLength) {
30501             Roo.log('Invalid Exif data: Invalid directory size.');
30502             return;
30503         }
30504         for (i = 0; i < tagsNumber; i += 1) {
30505             this.parseExifTag(
30506                 dataView,
30507                 tiffOffset,
30508                 dirOffset + 2 + 12 * i, // tag offset
30509                 littleEndian
30510             );
30511         }
30512         // Return the offset to the next directory:
30513         return dataView.getUint32(dirEndOffset, littleEndian);
30514     },
30515     
30516     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30517     {
30518         var tag = dataView.getUint16(offset, littleEndian);
30519         
30520         this.exif[tag] = this.getExifValue(
30521             dataView,
30522             tiffOffset,
30523             offset,
30524             dataView.getUint16(offset + 2, littleEndian), // tag type
30525             dataView.getUint32(offset + 4, littleEndian), // tag length
30526             littleEndian
30527         );
30528     },
30529     
30530     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30531     {
30532         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30533             tagSize,
30534             dataOffset,
30535             values,
30536             i,
30537             str,
30538             c;
30539     
30540         if (!tagType) {
30541             Roo.log('Invalid Exif data: Invalid tag type.');
30542             return;
30543         }
30544         
30545         tagSize = tagType.size * length;
30546         // Determine if the value is contained in the dataOffset bytes,
30547         // or if the value at the dataOffset is a pointer to the actual data:
30548         dataOffset = tagSize > 4 ?
30549                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30550         if (dataOffset + tagSize > dataView.byteLength) {
30551             Roo.log('Invalid Exif data: Invalid data offset.');
30552             return;
30553         }
30554         if (length === 1) {
30555             return tagType.getValue(dataView, dataOffset, littleEndian);
30556         }
30557         values = [];
30558         for (i = 0; i < length; i += 1) {
30559             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30560         }
30561         
30562         if (tagType.ascii) {
30563             str = '';
30564             // Concatenate the chars:
30565             for (i = 0; i < values.length; i += 1) {
30566                 c = values[i];
30567                 // Ignore the terminating NULL byte(s):
30568                 if (c === '\u0000') {
30569                     break;
30570                 }
30571                 str += c;
30572             }
30573             return str;
30574         }
30575         return values;
30576     }
30577     
30578 });
30579
30580 Roo.apply(Roo.bootstrap.UploadCropbox, {
30581     tags : {
30582         'Orientation': 0x0112
30583     },
30584     
30585     Orientation: {
30586             1: 0, //'top-left',
30587 //            2: 'top-right',
30588             3: 180, //'bottom-right',
30589 //            4: 'bottom-left',
30590 //            5: 'left-top',
30591             6: 90, //'right-top',
30592 //            7: 'right-bottom',
30593             8: 270 //'left-bottom'
30594     },
30595     
30596     exifTagTypes : {
30597         // byte, 8-bit unsigned int:
30598         1: {
30599             getValue: function (dataView, dataOffset) {
30600                 return dataView.getUint8(dataOffset);
30601             },
30602             size: 1
30603         },
30604         // ascii, 8-bit byte:
30605         2: {
30606             getValue: function (dataView, dataOffset) {
30607                 return String.fromCharCode(dataView.getUint8(dataOffset));
30608             },
30609             size: 1,
30610             ascii: true
30611         },
30612         // short, 16 bit int:
30613         3: {
30614             getValue: function (dataView, dataOffset, littleEndian) {
30615                 return dataView.getUint16(dataOffset, littleEndian);
30616             },
30617             size: 2
30618         },
30619         // long, 32 bit int:
30620         4: {
30621             getValue: function (dataView, dataOffset, littleEndian) {
30622                 return dataView.getUint32(dataOffset, littleEndian);
30623             },
30624             size: 4
30625         },
30626         // rational = two long values, first is numerator, second is denominator:
30627         5: {
30628             getValue: function (dataView, dataOffset, littleEndian) {
30629                 return dataView.getUint32(dataOffset, littleEndian) /
30630                     dataView.getUint32(dataOffset + 4, littleEndian);
30631             },
30632             size: 8
30633         },
30634         // slong, 32 bit signed int:
30635         9: {
30636             getValue: function (dataView, dataOffset, littleEndian) {
30637                 return dataView.getInt32(dataOffset, littleEndian);
30638             },
30639             size: 4
30640         },
30641         // srational, two slongs, first is numerator, second is denominator:
30642         10: {
30643             getValue: function (dataView, dataOffset, littleEndian) {
30644                 return dataView.getInt32(dataOffset, littleEndian) /
30645                     dataView.getInt32(dataOffset + 4, littleEndian);
30646             },
30647             size: 8
30648         }
30649     },
30650     
30651     footer : {
30652         STANDARD : [
30653             {
30654                 tag : 'div',
30655                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30656                 action : 'rotate-left',
30657                 cn : [
30658                     {
30659                         tag : 'button',
30660                         cls : 'btn btn-default',
30661                         html : '<i class="fa fa-undo"></i>'
30662                     }
30663                 ]
30664             },
30665             {
30666                 tag : 'div',
30667                 cls : 'btn-group roo-upload-cropbox-picture',
30668                 action : 'picture',
30669                 cn : [
30670                     {
30671                         tag : 'button',
30672                         cls : 'btn btn-default',
30673                         html : '<i class="fa fa-picture-o"></i>'
30674                     }
30675                 ]
30676             },
30677             {
30678                 tag : 'div',
30679                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30680                 action : 'rotate-right',
30681                 cn : [
30682                     {
30683                         tag : 'button',
30684                         cls : 'btn btn-default',
30685                         html : '<i class="fa fa-repeat"></i>'
30686                     }
30687                 ]
30688             }
30689         ],
30690         DOCUMENT : [
30691             {
30692                 tag : 'div',
30693                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30694                 action : 'rotate-left',
30695                 cn : [
30696                     {
30697                         tag : 'button',
30698                         cls : 'btn btn-default',
30699                         html : '<i class="fa fa-undo"></i>'
30700                     }
30701                 ]
30702             },
30703             {
30704                 tag : 'div',
30705                 cls : 'btn-group roo-upload-cropbox-download',
30706                 action : 'download',
30707                 cn : [
30708                     {
30709                         tag : 'button',
30710                         cls : 'btn btn-default',
30711                         html : '<i class="fa fa-download"></i>'
30712                     }
30713                 ]
30714             },
30715             {
30716                 tag : 'div',
30717                 cls : 'btn-group roo-upload-cropbox-crop',
30718                 action : 'crop',
30719                 cn : [
30720                     {
30721                         tag : 'button',
30722                         cls : 'btn btn-default',
30723                         html : '<i class="fa fa-crop"></i>'
30724                     }
30725                 ]
30726             },
30727             {
30728                 tag : 'div',
30729                 cls : 'btn-group roo-upload-cropbox-trash',
30730                 action : 'trash',
30731                 cn : [
30732                     {
30733                         tag : 'button',
30734                         cls : 'btn btn-default',
30735                         html : '<i class="fa fa-trash"></i>'
30736                     }
30737                 ]
30738             },
30739             {
30740                 tag : 'div',
30741                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30742                 action : 'rotate-right',
30743                 cn : [
30744                     {
30745                         tag : 'button',
30746                         cls : 'btn btn-default',
30747                         html : '<i class="fa fa-repeat"></i>'
30748                     }
30749                 ]
30750             }
30751         ],
30752         ROTATOR : [
30753             {
30754                 tag : 'div',
30755                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30756                 action : 'rotate-left',
30757                 cn : [
30758                     {
30759                         tag : 'button',
30760                         cls : 'btn btn-default',
30761                         html : '<i class="fa fa-undo"></i>'
30762                     }
30763                 ]
30764             },
30765             {
30766                 tag : 'div',
30767                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30768                 action : 'rotate-right',
30769                 cn : [
30770                     {
30771                         tag : 'button',
30772                         cls : 'btn btn-default',
30773                         html : '<i class="fa fa-repeat"></i>'
30774                     }
30775                 ]
30776             }
30777         ]
30778     }
30779 });
30780
30781 /*
30782 * Licence: LGPL
30783 */
30784
30785 /**
30786  * @class Roo.bootstrap.DocumentManager
30787  * @extends Roo.bootstrap.Component
30788  * Bootstrap DocumentManager class
30789  * @cfg {String} paramName default 'imageUpload'
30790  * @cfg {String} toolTipName default 'filename'
30791  * @cfg {String} method default POST
30792  * @cfg {String} url action url
30793  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30794  * @cfg {Boolean} multiple multiple upload default true
30795  * @cfg {Number} thumbSize default 300
30796  * @cfg {String} fieldLabel
30797  * @cfg {Number} labelWidth default 4
30798  * @cfg {String} labelAlign (left|top) default left
30799  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30800 * @cfg {Number} labellg set the width of label (1-12)
30801  * @cfg {Number} labelmd set the width of label (1-12)
30802  * @cfg {Number} labelsm set the width of label (1-12)
30803  * @cfg {Number} labelxs set the width of label (1-12)
30804  * 
30805  * @constructor
30806  * Create a new DocumentManager
30807  * @param {Object} config The config object
30808  */
30809
30810 Roo.bootstrap.DocumentManager = function(config){
30811     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30812     
30813     this.files = [];
30814     this.delegates = [];
30815     
30816     this.addEvents({
30817         /**
30818          * @event initial
30819          * Fire when initial the DocumentManager
30820          * @param {Roo.bootstrap.DocumentManager} this
30821          */
30822         "initial" : true,
30823         /**
30824          * @event inspect
30825          * inspect selected file
30826          * @param {Roo.bootstrap.DocumentManager} this
30827          * @param {File} file
30828          */
30829         "inspect" : true,
30830         /**
30831          * @event exception
30832          * Fire when xhr load exception
30833          * @param {Roo.bootstrap.DocumentManager} this
30834          * @param {XMLHttpRequest} xhr
30835          */
30836         "exception" : true,
30837         /**
30838          * @event afterupload
30839          * Fire when xhr load exception
30840          * @param {Roo.bootstrap.DocumentManager} this
30841          * @param {XMLHttpRequest} xhr
30842          */
30843         "afterupload" : true,
30844         /**
30845          * @event prepare
30846          * prepare the form data
30847          * @param {Roo.bootstrap.DocumentManager} this
30848          * @param {Object} formData
30849          */
30850         "prepare" : true,
30851         /**
30852          * @event remove
30853          * Fire when remove the file
30854          * @param {Roo.bootstrap.DocumentManager} this
30855          * @param {Object} file
30856          */
30857         "remove" : true,
30858         /**
30859          * @event refresh
30860          * Fire after refresh the file
30861          * @param {Roo.bootstrap.DocumentManager} this
30862          */
30863         "refresh" : true,
30864         /**
30865          * @event click
30866          * Fire after click the image
30867          * @param {Roo.bootstrap.DocumentManager} this
30868          * @param {Object} file
30869          */
30870         "click" : true,
30871         /**
30872          * @event edit
30873          * Fire when upload a image and editable set to true
30874          * @param {Roo.bootstrap.DocumentManager} this
30875          * @param {Object} file
30876          */
30877         "edit" : true,
30878         /**
30879          * @event beforeselectfile
30880          * Fire before select file
30881          * @param {Roo.bootstrap.DocumentManager} this
30882          */
30883         "beforeselectfile" : true,
30884         /**
30885          * @event process
30886          * Fire before process file
30887          * @param {Roo.bootstrap.DocumentManager} this
30888          * @param {Object} file
30889          */
30890         "process" : true,
30891         /**
30892          * @event previewrendered
30893          * Fire when preview rendered
30894          * @param {Roo.bootstrap.DocumentManager} this
30895          * @param {Object} file
30896          */
30897         "previewrendered" : true,
30898         /**
30899          */
30900         "previewResize" : true
30901         
30902     });
30903 };
30904
30905 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
30906     
30907     boxes : 0,
30908     inputName : '',
30909     thumbSize : 300,
30910     multiple : true,
30911     files : false,
30912     method : 'POST',
30913     url : '',
30914     paramName : 'imageUpload',
30915     toolTipName : 'filename',
30916     fieldLabel : '',
30917     labelWidth : 4,
30918     labelAlign : 'left',
30919     editable : true,
30920     delegates : false,
30921     xhr : false, 
30922     
30923     labellg : 0,
30924     labelmd : 0,
30925     labelsm : 0,
30926     labelxs : 0,
30927     
30928     getAutoCreate : function()
30929     {   
30930         var managerWidget = {
30931             tag : 'div',
30932             cls : 'roo-document-manager',
30933             cn : [
30934                 {
30935                     tag : 'input',
30936                     cls : 'roo-document-manager-selector',
30937                     type : 'file'
30938                 },
30939                 {
30940                     tag : 'div',
30941                     cls : 'roo-document-manager-uploader',
30942                     cn : [
30943                         {
30944                             tag : 'div',
30945                             cls : 'roo-document-manager-upload-btn',
30946                             html : '<i class="fa fa-plus"></i>'
30947                         }
30948                     ]
30949                     
30950                 }
30951             ]
30952         };
30953         
30954         var content = [
30955             {
30956                 tag : 'div',
30957                 cls : 'column col-md-12',
30958                 cn : managerWidget
30959             }
30960         ];
30961         
30962         if(this.fieldLabel.length){
30963             
30964             content = [
30965                 {
30966                     tag : 'div',
30967                     cls : 'column col-md-12',
30968                     html : this.fieldLabel
30969                 },
30970                 {
30971                     tag : 'div',
30972                     cls : 'column col-md-12',
30973                     cn : managerWidget
30974                 }
30975             ];
30976
30977             if(this.labelAlign == 'left'){
30978                 content = [
30979                     {
30980                         tag : 'div',
30981                         cls : 'column',
30982                         html : this.fieldLabel
30983                     },
30984                     {
30985                         tag : 'div',
30986                         cls : 'column',
30987                         cn : managerWidget
30988                     }
30989                 ];
30990                 
30991                 if(this.labelWidth > 12){
30992                     content[0].style = "width: " + this.labelWidth + 'px';
30993                 }
30994
30995                 if(this.labelWidth < 13 && this.labelmd == 0){
30996                     this.labelmd = this.labelWidth;
30997                 }
30998
30999                 if(this.labellg > 0){
31000                     content[0].cls += ' col-lg-' + this.labellg;
31001                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31002                 }
31003
31004                 if(this.labelmd > 0){
31005                     content[0].cls += ' col-md-' + this.labelmd;
31006                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31007                 }
31008
31009                 if(this.labelsm > 0){
31010                     content[0].cls += ' col-sm-' + this.labelsm;
31011                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31012                 }
31013
31014                 if(this.labelxs > 0){
31015                     content[0].cls += ' col-xs-' + this.labelxs;
31016                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31017                 }
31018                 
31019             }
31020         }
31021         
31022         var cfg = {
31023             tag : 'div',
31024             cls : 'row clearfix',
31025             cn : content
31026         };
31027         
31028         return cfg;
31029         
31030     },
31031     
31032     initEvents : function()
31033     {
31034         this.managerEl = this.el.select('.roo-document-manager', true).first();
31035         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31036         
31037         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31038         this.selectorEl.hide();
31039         
31040         if(this.multiple){
31041             this.selectorEl.attr('multiple', 'multiple');
31042         }
31043         
31044         this.selectorEl.on('change', this.onFileSelected, this);
31045         
31046         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31047         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31048         
31049         this.uploader.on('click', this.onUploaderClick, this);
31050         
31051         this.renderProgressDialog();
31052         
31053         var _this = this;
31054         
31055         window.addEventListener("resize", function() { _this.refresh(); } );
31056         
31057         this.fireEvent('initial', this);
31058     },
31059     
31060     renderProgressDialog : function()
31061     {
31062         var _this = this;
31063         
31064         this.progressDialog = new Roo.bootstrap.Modal({
31065             cls : 'roo-document-manager-progress-dialog',
31066             allow_close : false,
31067             animate : false,
31068             title : '',
31069             buttons : [
31070                 {
31071                     name  :'cancel',
31072                     weight : 'danger',
31073                     html : 'Cancel'
31074                 }
31075             ], 
31076             listeners : { 
31077                 btnclick : function() {
31078                     _this.uploadCancel();
31079                     this.hide();
31080                 }
31081             }
31082         });
31083          
31084         this.progressDialog.render(Roo.get(document.body));
31085          
31086         this.progress = new Roo.bootstrap.Progress({
31087             cls : 'roo-document-manager-progress',
31088             active : true,
31089             striped : true
31090         });
31091         
31092         this.progress.render(this.progressDialog.getChildContainer());
31093         
31094         this.progressBar = new Roo.bootstrap.ProgressBar({
31095             cls : 'roo-document-manager-progress-bar',
31096             aria_valuenow : 0,
31097             aria_valuemin : 0,
31098             aria_valuemax : 12,
31099             panel : 'success'
31100         });
31101         
31102         this.progressBar.render(this.progress.getChildContainer());
31103     },
31104     
31105     onUploaderClick : function(e)
31106     {
31107         e.preventDefault();
31108      
31109         if(this.fireEvent('beforeselectfile', this) != false){
31110             this.selectorEl.dom.click();
31111         }
31112         
31113     },
31114     
31115     onFileSelected : function(e)
31116     {
31117         e.preventDefault();
31118         
31119         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31120             return;
31121         }
31122         
31123         Roo.each(this.selectorEl.dom.files, function(file){
31124             if(this.fireEvent('inspect', this, file) != false){
31125                 this.files.push(file);
31126             }
31127         }, this);
31128         
31129         this.queue();
31130         
31131     },
31132     
31133     queue : function()
31134     {
31135         this.selectorEl.dom.value = '';
31136         
31137         if(!this.files || !this.files.length){
31138             return;
31139         }
31140         
31141         if(this.boxes > 0 && this.files.length > this.boxes){
31142             this.files = this.files.slice(0, this.boxes);
31143         }
31144         
31145         this.uploader.show();
31146         
31147         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31148             this.uploader.hide();
31149         }
31150         
31151         var _this = this;
31152         
31153         var files = [];
31154         
31155         var docs = [];
31156         
31157         Roo.each(this.files, function(file){
31158             
31159             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31160                 var f = this.renderPreview(file);
31161                 files.push(f);
31162                 return;
31163             }
31164             
31165             if(file.type.indexOf('image') != -1){
31166                 this.delegates.push(
31167                     (function(){
31168                         _this.process(file);
31169                     }).createDelegate(this)
31170                 );
31171         
31172                 return;
31173             }
31174             
31175             docs.push(
31176                 (function(){
31177                     _this.process(file);
31178                 }).createDelegate(this)
31179             );
31180             
31181         }, this);
31182         
31183         this.files = files;
31184         
31185         this.delegates = this.delegates.concat(docs);
31186         
31187         if(!this.delegates.length){
31188             this.refresh();
31189             return;
31190         }
31191         
31192         this.progressBar.aria_valuemax = this.delegates.length;
31193         
31194         this.arrange();
31195         
31196         return;
31197     },
31198     
31199     arrange : function()
31200     {
31201         if(!this.delegates.length){
31202             this.progressDialog.hide();
31203             this.refresh();
31204             return;
31205         }
31206         
31207         var delegate = this.delegates.shift();
31208         
31209         this.progressDialog.show();
31210         
31211         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31212         
31213         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31214         
31215         delegate();
31216     },
31217     
31218     refresh : function()
31219     {
31220         this.uploader.show();
31221         
31222         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31223             this.uploader.hide();
31224         }
31225         
31226         Roo.isTouch ? this.closable(false) : this.closable(true);
31227         
31228         this.fireEvent('refresh', this);
31229     },
31230     
31231     onRemove : function(e, el, o)
31232     {
31233         e.preventDefault();
31234         
31235         this.fireEvent('remove', this, o);
31236         
31237     },
31238     
31239     remove : function(o)
31240     {
31241         var files = [];
31242         
31243         Roo.each(this.files, function(file){
31244             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31245                 files.push(file);
31246                 return;
31247             }
31248
31249             o.target.remove();
31250
31251         }, this);
31252         
31253         this.files = files;
31254         
31255         this.refresh();
31256     },
31257     
31258     clear : function()
31259     {
31260         Roo.each(this.files, function(file){
31261             if(!file.target){
31262                 return;
31263             }
31264             
31265             file.target.remove();
31266
31267         }, this);
31268         
31269         this.files = [];
31270         
31271         this.refresh();
31272     },
31273     
31274     onClick : function(e, el, o)
31275     {
31276         e.preventDefault();
31277         
31278         this.fireEvent('click', this, o);
31279         
31280     },
31281     
31282     closable : function(closable)
31283     {
31284         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31285             
31286             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31287             
31288             if(closable){
31289                 el.show();
31290                 return;
31291             }
31292             
31293             el.hide();
31294             
31295         }, this);
31296     },
31297     
31298     xhrOnLoad : function(xhr)
31299     {
31300         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31301             el.remove();
31302         }, this);
31303         
31304         if (xhr.readyState !== 4) {
31305             this.arrange();
31306             this.fireEvent('exception', this, xhr);
31307             return;
31308         }
31309
31310         var response = Roo.decode(xhr.responseText);
31311         
31312         if(!response.success){
31313             this.arrange();
31314             this.fireEvent('exception', this, xhr);
31315             return;
31316         }
31317         
31318         var file = this.renderPreview(response.data);
31319         
31320         this.files.push(file);
31321         
31322         this.arrange();
31323         
31324         this.fireEvent('afterupload', this, xhr);
31325         
31326     },
31327     
31328     xhrOnError : function(xhr)
31329     {
31330         Roo.log('xhr on error');
31331         
31332         var response = Roo.decode(xhr.responseText);
31333           
31334         Roo.log(response);
31335         
31336         this.arrange();
31337     },
31338     
31339     process : function(file)
31340     {
31341         if(this.fireEvent('process', this, file) !== false){
31342             if(this.editable && file.type.indexOf('image') != -1){
31343                 this.fireEvent('edit', this, file);
31344                 return;
31345             }
31346
31347             this.uploadStart(file, false);
31348
31349             return;
31350         }
31351         
31352     },
31353     
31354     uploadStart : function(file, crop)
31355     {
31356         this.xhr = new XMLHttpRequest();
31357         
31358         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31359             this.arrange();
31360             return;
31361         }
31362         
31363         file.xhr = this.xhr;
31364             
31365         this.managerEl.createChild({
31366             tag : 'div',
31367             cls : 'roo-document-manager-loading',
31368             cn : [
31369                 {
31370                     tag : 'div',
31371                     tooltip : file.name,
31372                     cls : 'roo-document-manager-thumb',
31373                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31374                 }
31375             ]
31376
31377         });
31378
31379         this.xhr.open(this.method, this.url, true);
31380         
31381         var headers = {
31382             "Accept": "application/json",
31383             "Cache-Control": "no-cache",
31384             "X-Requested-With": "XMLHttpRequest"
31385         };
31386         
31387         for (var headerName in headers) {
31388             var headerValue = headers[headerName];
31389             if (headerValue) {
31390                 this.xhr.setRequestHeader(headerName, headerValue);
31391             }
31392         }
31393         
31394         var _this = this;
31395         
31396         this.xhr.onload = function()
31397         {
31398             _this.xhrOnLoad(_this.xhr);
31399         }
31400         
31401         this.xhr.onerror = function()
31402         {
31403             _this.xhrOnError(_this.xhr);
31404         }
31405         
31406         var formData = new FormData();
31407
31408         formData.append('returnHTML', 'NO');
31409         
31410         if(crop){
31411             formData.append('crop', crop);
31412         }
31413         
31414         formData.append(this.paramName, file, file.name);
31415         
31416         var options = {
31417             file : file, 
31418             manually : false
31419         };
31420         
31421         if(this.fireEvent('prepare', this, formData, options) != false){
31422             
31423             if(options.manually){
31424                 return;
31425             }
31426             
31427             this.xhr.send(formData);
31428             return;
31429         };
31430         
31431         this.uploadCancel();
31432     },
31433     
31434     uploadCancel : function()
31435     {
31436         if (this.xhr) {
31437             this.xhr.abort();
31438         }
31439         
31440         this.delegates = [];
31441         
31442         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31443             el.remove();
31444         }, this);
31445         
31446         this.arrange();
31447     },
31448     
31449     renderPreview : function(file)
31450     {
31451         if(typeof(file.target) != 'undefined' && file.target){
31452             return file;
31453         }
31454         
31455         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31456         
31457         var previewEl = this.managerEl.createChild({
31458             tag : 'div',
31459             cls : 'roo-document-manager-preview',
31460             cn : [
31461                 {
31462                     tag : 'div',
31463                     tooltip : file[this.toolTipName],
31464                     cls : 'roo-document-manager-thumb',
31465                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31466                 },
31467                 {
31468                     tag : 'button',
31469                     cls : 'close',
31470                     html : '<i class="fa fa-times-circle"></i>'
31471                 }
31472             ]
31473         });
31474
31475         var close = previewEl.select('button.close', true).first();
31476
31477         close.on('click', this.onRemove, this, file);
31478
31479         file.target = previewEl;
31480
31481         var image = previewEl.select('img', true).first();
31482         
31483         var _this = this;
31484         
31485         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31486         
31487         image.on('click', this.onClick, this, file);
31488         
31489         this.fireEvent('previewrendered', this, file);
31490         
31491         return file;
31492         
31493     },
31494     
31495     onPreviewLoad : function(file, image)
31496     {
31497         if(typeof(file.target) == 'undefined' || !file.target){
31498             return;
31499         }
31500         
31501         var width = image.dom.naturalWidth || image.dom.width;
31502         var height = image.dom.naturalHeight || image.dom.height;
31503         
31504         if(!this.previewResize) {
31505             return;
31506         }
31507         
31508         if(width > height){
31509             file.target.addClass('wide');
31510             return;
31511         }
31512         
31513         file.target.addClass('tall');
31514         return;
31515         
31516     },
31517     
31518     uploadFromSource : function(file, crop)
31519     {
31520         this.xhr = new XMLHttpRequest();
31521         
31522         this.managerEl.createChild({
31523             tag : 'div',
31524             cls : 'roo-document-manager-loading',
31525             cn : [
31526                 {
31527                     tag : 'div',
31528                     tooltip : file.name,
31529                     cls : 'roo-document-manager-thumb',
31530                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31531                 }
31532             ]
31533
31534         });
31535
31536         this.xhr.open(this.method, this.url, true);
31537         
31538         var headers = {
31539             "Accept": "application/json",
31540             "Cache-Control": "no-cache",
31541             "X-Requested-With": "XMLHttpRequest"
31542         };
31543         
31544         for (var headerName in headers) {
31545             var headerValue = headers[headerName];
31546             if (headerValue) {
31547                 this.xhr.setRequestHeader(headerName, headerValue);
31548             }
31549         }
31550         
31551         var _this = this;
31552         
31553         this.xhr.onload = function()
31554         {
31555             _this.xhrOnLoad(_this.xhr);
31556         }
31557         
31558         this.xhr.onerror = function()
31559         {
31560             _this.xhrOnError(_this.xhr);
31561         }
31562         
31563         var formData = new FormData();
31564
31565         formData.append('returnHTML', 'NO');
31566         
31567         formData.append('crop', crop);
31568         
31569         if(typeof(file.filename) != 'undefined'){
31570             formData.append('filename', file.filename);
31571         }
31572         
31573         if(typeof(file.mimetype) != 'undefined'){
31574             formData.append('mimetype', file.mimetype);
31575         }
31576         
31577         Roo.log(formData);
31578         
31579         if(this.fireEvent('prepare', this, formData) != false){
31580             this.xhr.send(formData);
31581         };
31582     }
31583 });
31584
31585 /*
31586 * Licence: LGPL
31587 */
31588
31589 /**
31590  * @class Roo.bootstrap.DocumentViewer
31591  * @extends Roo.bootstrap.Component
31592  * Bootstrap DocumentViewer class
31593  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31594  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31595  * 
31596  * @constructor
31597  * Create a new DocumentViewer
31598  * @param {Object} config The config object
31599  */
31600
31601 Roo.bootstrap.DocumentViewer = function(config){
31602     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31603     
31604     this.addEvents({
31605         /**
31606          * @event initial
31607          * Fire after initEvent
31608          * @param {Roo.bootstrap.DocumentViewer} this
31609          */
31610         "initial" : true,
31611         /**
31612          * @event click
31613          * Fire after click
31614          * @param {Roo.bootstrap.DocumentViewer} this
31615          */
31616         "click" : true,
31617         /**
31618          * @event download
31619          * Fire after download button
31620          * @param {Roo.bootstrap.DocumentViewer} this
31621          */
31622         "download" : true,
31623         /**
31624          * @event trash
31625          * Fire after trash button
31626          * @param {Roo.bootstrap.DocumentViewer} this
31627          */
31628         "trash" : true
31629         
31630     });
31631 };
31632
31633 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31634     
31635     showDownload : true,
31636     
31637     showTrash : true,
31638     
31639     getAutoCreate : function()
31640     {
31641         var cfg = {
31642             tag : 'div',
31643             cls : 'roo-document-viewer',
31644             cn : [
31645                 {
31646                     tag : 'div',
31647                     cls : 'roo-document-viewer-body',
31648                     cn : [
31649                         {
31650                             tag : 'div',
31651                             cls : 'roo-document-viewer-thumb',
31652                             cn : [
31653                                 {
31654                                     tag : 'img',
31655                                     cls : 'roo-document-viewer-image'
31656                                 }
31657                             ]
31658                         }
31659                     ]
31660                 },
31661                 {
31662                     tag : 'div',
31663                     cls : 'roo-document-viewer-footer',
31664                     cn : {
31665                         tag : 'div',
31666                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31667                         cn : [
31668                             {
31669                                 tag : 'div',
31670                                 cls : 'btn-group roo-document-viewer-download',
31671                                 cn : [
31672                                     {
31673                                         tag : 'button',
31674                                         cls : 'btn btn-default',
31675                                         html : '<i class="fa fa-download"></i>'
31676                                     }
31677                                 ]
31678                             },
31679                             {
31680                                 tag : 'div',
31681                                 cls : 'btn-group roo-document-viewer-trash',
31682                                 cn : [
31683                                     {
31684                                         tag : 'button',
31685                                         cls : 'btn btn-default',
31686                                         html : '<i class="fa fa-trash"></i>'
31687                                     }
31688                                 ]
31689                             }
31690                         ]
31691                     }
31692                 }
31693             ]
31694         };
31695         
31696         return cfg;
31697     },
31698     
31699     initEvents : function()
31700     {
31701         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31702         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31703         
31704         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31705         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31706         
31707         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31708         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31709         
31710         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31711         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31712         
31713         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31714         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31715         
31716         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31717         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31718         
31719         this.bodyEl.on('click', this.onClick, this);
31720         this.downloadBtn.on('click', this.onDownload, this);
31721         this.trashBtn.on('click', this.onTrash, this);
31722         
31723         this.downloadBtn.hide();
31724         this.trashBtn.hide();
31725         
31726         if(this.showDownload){
31727             this.downloadBtn.show();
31728         }
31729         
31730         if(this.showTrash){
31731             this.trashBtn.show();
31732         }
31733         
31734         if(!this.showDownload && !this.showTrash) {
31735             this.footerEl.hide();
31736         }
31737         
31738     },
31739     
31740     initial : function()
31741     {
31742         this.fireEvent('initial', this);
31743         
31744     },
31745     
31746     onClick : function(e)
31747     {
31748         e.preventDefault();
31749         
31750         this.fireEvent('click', this);
31751     },
31752     
31753     onDownload : function(e)
31754     {
31755         e.preventDefault();
31756         
31757         this.fireEvent('download', this);
31758     },
31759     
31760     onTrash : function(e)
31761     {
31762         e.preventDefault();
31763         
31764         this.fireEvent('trash', this);
31765     }
31766     
31767 });
31768 /*
31769  * - LGPL
31770  *
31771  * nav progress bar
31772  * 
31773  */
31774
31775 /**
31776  * @class Roo.bootstrap.NavProgressBar
31777  * @extends Roo.bootstrap.Component
31778  * Bootstrap NavProgressBar class
31779  * 
31780  * @constructor
31781  * Create a new nav progress bar
31782  * @param {Object} config The config object
31783  */
31784
31785 Roo.bootstrap.NavProgressBar = function(config){
31786     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31787
31788     this.bullets = this.bullets || [];
31789    
31790 //    Roo.bootstrap.NavProgressBar.register(this);
31791      this.addEvents({
31792         /**
31793              * @event changed
31794              * Fires when the active item changes
31795              * @param {Roo.bootstrap.NavProgressBar} this
31796              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31797              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31798          */
31799         'changed': true
31800      });
31801     
31802 };
31803
31804 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31805     
31806     bullets : [],
31807     barItems : [],
31808     
31809     getAutoCreate : function()
31810     {
31811         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31812         
31813         cfg = {
31814             tag : 'div',
31815             cls : 'roo-navigation-bar-group',
31816             cn : [
31817                 {
31818                     tag : 'div',
31819                     cls : 'roo-navigation-top-bar'
31820                 },
31821                 {
31822                     tag : 'div',
31823                     cls : 'roo-navigation-bullets-bar',
31824                     cn : [
31825                         {
31826                             tag : 'ul',
31827                             cls : 'roo-navigation-bar'
31828                         }
31829                     ]
31830                 },
31831                 
31832                 {
31833                     tag : 'div',
31834                     cls : 'roo-navigation-bottom-bar'
31835                 }
31836             ]
31837             
31838         };
31839         
31840         return cfg;
31841         
31842     },
31843     
31844     initEvents: function() 
31845     {
31846         
31847     },
31848     
31849     onRender : function(ct, position) 
31850     {
31851         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
31852         
31853         if(this.bullets.length){
31854             Roo.each(this.bullets, function(b){
31855                this.addItem(b);
31856             }, this);
31857         }
31858         
31859         this.format();
31860         
31861     },
31862     
31863     addItem : function(cfg)
31864     {
31865         var item = new Roo.bootstrap.NavProgressItem(cfg);
31866         
31867         item.parentId = this.id;
31868         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
31869         
31870         if(cfg.html){
31871             var top = new Roo.bootstrap.Element({
31872                 tag : 'div',
31873                 cls : 'roo-navigation-bar-text'
31874             });
31875             
31876             var bottom = new Roo.bootstrap.Element({
31877                 tag : 'div',
31878                 cls : 'roo-navigation-bar-text'
31879             });
31880             
31881             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
31882             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
31883             
31884             var topText = new Roo.bootstrap.Element({
31885                 tag : 'span',
31886                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
31887             });
31888             
31889             var bottomText = new Roo.bootstrap.Element({
31890                 tag : 'span',
31891                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
31892             });
31893             
31894             topText.onRender(top.el, null);
31895             bottomText.onRender(bottom.el, null);
31896             
31897             item.topEl = top;
31898             item.bottomEl = bottom;
31899         }
31900         
31901         this.barItems.push(item);
31902         
31903         return item;
31904     },
31905     
31906     getActive : function()
31907     {
31908         var active = false;
31909         
31910         Roo.each(this.barItems, function(v){
31911             
31912             if (!v.isActive()) {
31913                 return;
31914             }
31915             
31916             active = v;
31917             return false;
31918             
31919         });
31920         
31921         return active;
31922     },
31923     
31924     setActiveItem : function(item)
31925     {
31926         var prev = false;
31927         
31928         Roo.each(this.barItems, function(v){
31929             if (v.rid == item.rid) {
31930                 return ;
31931             }
31932             
31933             if (v.isActive()) {
31934                 v.setActive(false);
31935                 prev = v;
31936             }
31937         });
31938
31939         item.setActive(true);
31940         
31941         this.fireEvent('changed', this, item, prev);
31942     },
31943     
31944     getBarItem: function(rid)
31945     {
31946         var ret = false;
31947         
31948         Roo.each(this.barItems, function(e) {
31949             if (e.rid != rid) {
31950                 return;
31951             }
31952             
31953             ret =  e;
31954             return false;
31955         });
31956         
31957         return ret;
31958     },
31959     
31960     indexOfItem : function(item)
31961     {
31962         var index = false;
31963         
31964         Roo.each(this.barItems, function(v, i){
31965             
31966             if (v.rid != item.rid) {
31967                 return;
31968             }
31969             
31970             index = i;
31971             return false
31972         });
31973         
31974         return index;
31975     },
31976     
31977     setActiveNext : function()
31978     {
31979         var i = this.indexOfItem(this.getActive());
31980         
31981         if (i > this.barItems.length) {
31982             return;
31983         }
31984         
31985         this.setActiveItem(this.barItems[i+1]);
31986     },
31987     
31988     setActivePrev : function()
31989     {
31990         var i = this.indexOfItem(this.getActive());
31991         
31992         if (i  < 1) {
31993             return;
31994         }
31995         
31996         this.setActiveItem(this.barItems[i-1]);
31997     },
31998     
31999     format : function()
32000     {
32001         if(!this.barItems.length){
32002             return;
32003         }
32004      
32005         var width = 100 / this.barItems.length;
32006         
32007         Roo.each(this.barItems, function(i){
32008             i.el.setStyle('width', width + '%');
32009             i.topEl.el.setStyle('width', width + '%');
32010             i.bottomEl.el.setStyle('width', width + '%');
32011         }, this);
32012         
32013     }
32014     
32015 });
32016 /*
32017  * - LGPL
32018  *
32019  * Nav Progress Item
32020  * 
32021  */
32022
32023 /**
32024  * @class Roo.bootstrap.NavProgressItem
32025  * @extends Roo.bootstrap.Component
32026  * Bootstrap NavProgressItem class
32027  * @cfg {String} rid the reference id
32028  * @cfg {Boolean} active (true|false) Is item active default false
32029  * @cfg {Boolean} disabled (true|false) Is item active default false
32030  * @cfg {String} html
32031  * @cfg {String} position (top|bottom) text position default bottom
32032  * @cfg {String} icon show icon instead of number
32033  * 
32034  * @constructor
32035  * Create a new NavProgressItem
32036  * @param {Object} config The config object
32037  */
32038 Roo.bootstrap.NavProgressItem = function(config){
32039     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32040     this.addEvents({
32041         // raw events
32042         /**
32043          * @event click
32044          * The raw click event for the entire grid.
32045          * @param {Roo.bootstrap.NavProgressItem} this
32046          * @param {Roo.EventObject} e
32047          */
32048         "click" : true
32049     });
32050    
32051 };
32052
32053 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32054     
32055     rid : '',
32056     active : false,
32057     disabled : false,
32058     html : '',
32059     position : 'bottom',
32060     icon : false,
32061     
32062     getAutoCreate : function()
32063     {
32064         var iconCls = 'roo-navigation-bar-item-icon';
32065         
32066         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32067         
32068         var cfg = {
32069             tag: 'li',
32070             cls: 'roo-navigation-bar-item',
32071             cn : [
32072                 {
32073                     tag : 'i',
32074                     cls : iconCls
32075                 }
32076             ]
32077         };
32078         
32079         if(this.active){
32080             cfg.cls += ' active';
32081         }
32082         if(this.disabled){
32083             cfg.cls += ' disabled';
32084         }
32085         
32086         return cfg;
32087     },
32088     
32089     disable : function()
32090     {
32091         this.setDisabled(true);
32092     },
32093     
32094     enable : function()
32095     {
32096         this.setDisabled(false);
32097     },
32098     
32099     initEvents: function() 
32100     {
32101         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32102         
32103         this.iconEl.on('click', this.onClick, this);
32104     },
32105     
32106     onClick : function(e)
32107     {
32108         e.preventDefault();
32109         
32110         if(this.disabled){
32111             return;
32112         }
32113         
32114         if(this.fireEvent('click', this, e) === false){
32115             return;
32116         };
32117         
32118         this.parent().setActiveItem(this);
32119     },
32120     
32121     isActive: function () 
32122     {
32123         return this.active;
32124     },
32125     
32126     setActive : function(state)
32127     {
32128         if(this.active == state){
32129             return;
32130         }
32131         
32132         this.active = state;
32133         
32134         if (state) {
32135             this.el.addClass('active');
32136             return;
32137         }
32138         
32139         this.el.removeClass('active');
32140         
32141         return;
32142     },
32143     
32144     setDisabled : function(state)
32145     {
32146         if(this.disabled == state){
32147             return;
32148         }
32149         
32150         this.disabled = state;
32151         
32152         if (state) {
32153             this.el.addClass('disabled');
32154             return;
32155         }
32156         
32157         this.el.removeClass('disabled');
32158     },
32159     
32160     tooltipEl : function()
32161     {
32162         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32163     }
32164 });
32165  
32166
32167  /*
32168  * - LGPL
32169  *
32170  * FieldLabel
32171  * 
32172  */
32173
32174 /**
32175  * @class Roo.bootstrap.FieldLabel
32176  * @extends Roo.bootstrap.Component
32177  * Bootstrap FieldLabel class
32178  * @cfg {String} html contents of the element
32179  * @cfg {String} tag tag of the element default label
32180  * @cfg {String} cls class of the element
32181  * @cfg {String} target label target 
32182  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32183  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32184  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32185  * @cfg {String} iconTooltip default "This field is required"
32186  * @cfg {String} indicatorpos (left|right) default left
32187  * 
32188  * @constructor
32189  * Create a new FieldLabel
32190  * @param {Object} config The config object
32191  */
32192
32193 Roo.bootstrap.FieldLabel = function(config){
32194     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32195     
32196     this.addEvents({
32197             /**
32198              * @event invalid
32199              * Fires after the field has been marked as invalid.
32200              * @param {Roo.form.FieldLabel} this
32201              * @param {String} msg The validation message
32202              */
32203             invalid : true,
32204             /**
32205              * @event valid
32206              * Fires after the field has been validated with no errors.
32207              * @param {Roo.form.FieldLabel} this
32208              */
32209             valid : true
32210         });
32211 };
32212
32213 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32214     
32215     tag: 'label',
32216     cls: '',
32217     html: '',
32218     target: '',
32219     allowBlank : true,
32220     invalidClass : 'has-warning',
32221     validClass : 'has-success',
32222     iconTooltip : 'This field is required',
32223     indicatorpos : 'left',
32224     
32225     getAutoCreate : function(){
32226         
32227         var cls = "";
32228         if (!this.allowBlank) {
32229             cls  = "visible";
32230         }
32231         
32232         var cfg = {
32233             tag : this.tag,
32234             cls : 'roo-bootstrap-field-label ' + this.cls,
32235             for : this.target,
32236             cn : [
32237                 {
32238                     tag : 'i',
32239                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32240                     tooltip : this.iconTooltip
32241                 },
32242                 {
32243                     tag : 'span',
32244                     html : this.html
32245                 }
32246             ] 
32247         };
32248         
32249         if(this.indicatorpos == 'right'){
32250             var cfg = {
32251                 tag : this.tag,
32252                 cls : 'roo-bootstrap-field-label ' + this.cls,
32253                 for : this.target,
32254                 cn : [
32255                     {
32256                         tag : 'span',
32257                         html : this.html
32258                     },
32259                     {
32260                         tag : 'i',
32261                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32262                         tooltip : this.iconTooltip
32263                     }
32264                 ] 
32265             };
32266         }
32267         
32268         return cfg;
32269     },
32270     
32271     initEvents: function() 
32272     {
32273         Roo.bootstrap.Element.superclass.initEvents.call(this);
32274         
32275         this.indicator = this.indicatorEl();
32276         
32277         if(this.indicator){
32278             this.indicator.removeClass('visible');
32279             this.indicator.addClass('invisible');
32280         }
32281         
32282         Roo.bootstrap.FieldLabel.register(this);
32283     },
32284     
32285     indicatorEl : function()
32286     {
32287         var indicator = this.el.select('i.roo-required-indicator',true).first();
32288         
32289         if(!indicator){
32290             return false;
32291         }
32292         
32293         return indicator;
32294         
32295     },
32296     
32297     /**
32298      * Mark this field as valid
32299      */
32300     markValid : function()
32301     {
32302         if(this.indicator){
32303             this.indicator.removeClass('visible');
32304             this.indicator.addClass('invisible');
32305         }
32306         if (Roo.bootstrap.version == 3) {
32307             this.el.removeClass(this.invalidClass);
32308             this.el.addClass(this.validClass);
32309         } else {
32310             this.el.removeClass('is-invalid');
32311             this.el.addClass('is-valid');
32312         }
32313         
32314         
32315         this.fireEvent('valid', this);
32316     },
32317     
32318     /**
32319      * Mark this field as invalid
32320      * @param {String} msg The validation message
32321      */
32322     markInvalid : function(msg)
32323     {
32324         if(this.indicator){
32325             this.indicator.removeClass('invisible');
32326             this.indicator.addClass('visible');
32327         }
32328           if (Roo.bootstrap.version == 3) {
32329             this.el.removeClass(this.validClass);
32330             this.el.addClass(this.invalidClass);
32331         } else {
32332             this.el.removeClass('is-valid');
32333             this.el.addClass('is-invalid');
32334         }
32335         
32336         
32337         this.fireEvent('invalid', this, msg);
32338     }
32339     
32340    
32341 });
32342
32343 Roo.apply(Roo.bootstrap.FieldLabel, {
32344     
32345     groups: {},
32346     
32347      /**
32348     * register a FieldLabel Group
32349     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32350     */
32351     register : function(label)
32352     {
32353         if(this.groups.hasOwnProperty(label.target)){
32354             return;
32355         }
32356      
32357         this.groups[label.target] = label;
32358         
32359     },
32360     /**
32361     * fetch a FieldLabel Group based on the target
32362     * @param {string} target
32363     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32364     */
32365     get: function(target) {
32366         if (typeof(this.groups[target]) == 'undefined') {
32367             return false;
32368         }
32369         
32370         return this.groups[target] ;
32371     }
32372 });
32373
32374  
32375
32376  /*
32377  * - LGPL
32378  *
32379  * page DateSplitField.
32380  * 
32381  */
32382
32383
32384 /**
32385  * @class Roo.bootstrap.DateSplitField
32386  * @extends Roo.bootstrap.Component
32387  * Bootstrap DateSplitField class
32388  * @cfg {string} fieldLabel - the label associated
32389  * @cfg {Number} labelWidth set the width of label (0-12)
32390  * @cfg {String} labelAlign (top|left)
32391  * @cfg {Boolean} dayAllowBlank (true|false) default false
32392  * @cfg {Boolean} monthAllowBlank (true|false) default false
32393  * @cfg {Boolean} yearAllowBlank (true|false) default false
32394  * @cfg {string} dayPlaceholder 
32395  * @cfg {string} monthPlaceholder
32396  * @cfg {string} yearPlaceholder
32397  * @cfg {string} dayFormat default 'd'
32398  * @cfg {string} monthFormat default 'm'
32399  * @cfg {string} yearFormat default 'Y'
32400  * @cfg {Number} labellg set the width of label (1-12)
32401  * @cfg {Number} labelmd set the width of label (1-12)
32402  * @cfg {Number} labelsm set the width of label (1-12)
32403  * @cfg {Number} labelxs set the width of label (1-12)
32404
32405  *     
32406  * @constructor
32407  * Create a new DateSplitField
32408  * @param {Object} config The config object
32409  */
32410
32411 Roo.bootstrap.DateSplitField = function(config){
32412     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32413     
32414     this.addEvents({
32415         // raw events
32416          /**
32417          * @event years
32418          * getting the data of years
32419          * @param {Roo.bootstrap.DateSplitField} this
32420          * @param {Object} years
32421          */
32422         "years" : true,
32423         /**
32424          * @event days
32425          * getting the data of days
32426          * @param {Roo.bootstrap.DateSplitField} this
32427          * @param {Object} days
32428          */
32429         "days" : true,
32430         /**
32431          * @event invalid
32432          * Fires after the field has been marked as invalid.
32433          * @param {Roo.form.Field} this
32434          * @param {String} msg The validation message
32435          */
32436         invalid : true,
32437        /**
32438          * @event valid
32439          * Fires after the field has been validated with no errors.
32440          * @param {Roo.form.Field} this
32441          */
32442         valid : true
32443     });
32444 };
32445
32446 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32447     
32448     fieldLabel : '',
32449     labelAlign : 'top',
32450     labelWidth : 3,
32451     dayAllowBlank : false,
32452     monthAllowBlank : false,
32453     yearAllowBlank : false,
32454     dayPlaceholder : '',
32455     monthPlaceholder : '',
32456     yearPlaceholder : '',
32457     dayFormat : 'd',
32458     monthFormat : 'm',
32459     yearFormat : 'Y',
32460     isFormField : true,
32461     labellg : 0,
32462     labelmd : 0,
32463     labelsm : 0,
32464     labelxs : 0,
32465     
32466     getAutoCreate : function()
32467     {
32468         var cfg = {
32469             tag : 'div',
32470             cls : 'row roo-date-split-field-group',
32471             cn : [
32472                 {
32473                     tag : 'input',
32474                     type : 'hidden',
32475                     cls : 'form-hidden-field roo-date-split-field-group-value',
32476                     name : this.name
32477                 }
32478             ]
32479         };
32480         
32481         var labelCls = 'col-md-12';
32482         var contentCls = 'col-md-4';
32483         
32484         if(this.fieldLabel){
32485             
32486             var label = {
32487                 tag : 'div',
32488                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32489                 cn : [
32490                     {
32491                         tag : 'label',
32492                         html : this.fieldLabel
32493                     }
32494                 ]
32495             };
32496             
32497             if(this.labelAlign == 'left'){
32498             
32499                 if(this.labelWidth > 12){
32500                     label.style = "width: " + this.labelWidth + 'px';
32501                 }
32502
32503                 if(this.labelWidth < 13 && this.labelmd == 0){
32504                     this.labelmd = this.labelWidth;
32505                 }
32506
32507                 if(this.labellg > 0){
32508                     labelCls = ' col-lg-' + this.labellg;
32509                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32510                 }
32511
32512                 if(this.labelmd > 0){
32513                     labelCls = ' col-md-' + this.labelmd;
32514                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32515                 }
32516
32517                 if(this.labelsm > 0){
32518                     labelCls = ' col-sm-' + this.labelsm;
32519                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32520                 }
32521
32522                 if(this.labelxs > 0){
32523                     labelCls = ' col-xs-' + this.labelxs;
32524                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32525                 }
32526             }
32527             
32528             label.cls += ' ' + labelCls;
32529             
32530             cfg.cn.push(label);
32531         }
32532         
32533         Roo.each(['day', 'month', 'year'], function(t){
32534             cfg.cn.push({
32535                 tag : 'div',
32536                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32537             });
32538         }, this);
32539         
32540         return cfg;
32541     },
32542     
32543     inputEl: function ()
32544     {
32545         return this.el.select('.roo-date-split-field-group-value', true).first();
32546     },
32547     
32548     onRender : function(ct, position) 
32549     {
32550         var _this = this;
32551         
32552         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32553         
32554         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32555         
32556         this.dayField = new Roo.bootstrap.ComboBox({
32557             allowBlank : this.dayAllowBlank,
32558             alwaysQuery : true,
32559             displayField : 'value',
32560             editable : false,
32561             fieldLabel : '',
32562             forceSelection : true,
32563             mode : 'local',
32564             placeholder : this.dayPlaceholder,
32565             selectOnFocus : true,
32566             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32567             triggerAction : 'all',
32568             typeAhead : true,
32569             valueField : 'value',
32570             store : new Roo.data.SimpleStore({
32571                 data : (function() {    
32572                     var days = [];
32573                     _this.fireEvent('days', _this, days);
32574                     return days;
32575                 })(),
32576                 fields : [ 'value' ]
32577             }),
32578             listeners : {
32579                 select : function (_self, record, index)
32580                 {
32581                     _this.setValue(_this.getValue());
32582                 }
32583             }
32584         });
32585
32586         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32587         
32588         this.monthField = new Roo.bootstrap.MonthField({
32589             after : '<i class=\"fa fa-calendar\"></i>',
32590             allowBlank : this.monthAllowBlank,
32591             placeholder : this.monthPlaceholder,
32592             readOnly : true,
32593             listeners : {
32594                 render : function (_self)
32595                 {
32596                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32597                         e.preventDefault();
32598                         _self.focus();
32599                     });
32600                 },
32601                 select : function (_self, oldvalue, newvalue)
32602                 {
32603                     _this.setValue(_this.getValue());
32604                 }
32605             }
32606         });
32607         
32608         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32609         
32610         this.yearField = new Roo.bootstrap.ComboBox({
32611             allowBlank : this.yearAllowBlank,
32612             alwaysQuery : true,
32613             displayField : 'value',
32614             editable : false,
32615             fieldLabel : '',
32616             forceSelection : true,
32617             mode : 'local',
32618             placeholder : this.yearPlaceholder,
32619             selectOnFocus : true,
32620             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32621             triggerAction : 'all',
32622             typeAhead : true,
32623             valueField : 'value',
32624             store : new Roo.data.SimpleStore({
32625                 data : (function() {
32626                     var years = [];
32627                     _this.fireEvent('years', _this, years);
32628                     return years;
32629                 })(),
32630                 fields : [ 'value' ]
32631             }),
32632             listeners : {
32633                 select : function (_self, record, index)
32634                 {
32635                     _this.setValue(_this.getValue());
32636                 }
32637             }
32638         });
32639
32640         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32641     },
32642     
32643     setValue : function(v, format)
32644     {
32645         this.inputEl.dom.value = v;
32646         
32647         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32648         
32649         var d = Date.parseDate(v, f);
32650         
32651         if(!d){
32652             this.validate();
32653             return;
32654         }
32655         
32656         this.setDay(d.format(this.dayFormat));
32657         this.setMonth(d.format(this.monthFormat));
32658         this.setYear(d.format(this.yearFormat));
32659         
32660         this.validate();
32661         
32662         return;
32663     },
32664     
32665     setDay : function(v)
32666     {
32667         this.dayField.setValue(v);
32668         this.inputEl.dom.value = this.getValue();
32669         this.validate();
32670         return;
32671     },
32672     
32673     setMonth : function(v)
32674     {
32675         this.monthField.setValue(v, true);
32676         this.inputEl.dom.value = this.getValue();
32677         this.validate();
32678         return;
32679     },
32680     
32681     setYear : function(v)
32682     {
32683         this.yearField.setValue(v);
32684         this.inputEl.dom.value = this.getValue();
32685         this.validate();
32686         return;
32687     },
32688     
32689     getDay : function()
32690     {
32691         return this.dayField.getValue();
32692     },
32693     
32694     getMonth : function()
32695     {
32696         return this.monthField.getValue();
32697     },
32698     
32699     getYear : function()
32700     {
32701         return this.yearField.getValue();
32702     },
32703     
32704     getValue : function()
32705     {
32706         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32707         
32708         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32709         
32710         return date;
32711     },
32712     
32713     reset : function()
32714     {
32715         this.setDay('');
32716         this.setMonth('');
32717         this.setYear('');
32718         this.inputEl.dom.value = '';
32719         this.validate();
32720         return;
32721     },
32722     
32723     validate : function()
32724     {
32725         var d = this.dayField.validate();
32726         var m = this.monthField.validate();
32727         var y = this.yearField.validate();
32728         
32729         var valid = true;
32730         
32731         if(
32732                 (!this.dayAllowBlank && !d) ||
32733                 (!this.monthAllowBlank && !m) ||
32734                 (!this.yearAllowBlank && !y)
32735         ){
32736             valid = false;
32737         }
32738         
32739         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32740             return valid;
32741         }
32742         
32743         if(valid){
32744             this.markValid();
32745             return valid;
32746         }
32747         
32748         this.markInvalid();
32749         
32750         return valid;
32751     },
32752     
32753     markValid : function()
32754     {
32755         
32756         var label = this.el.select('label', true).first();
32757         var icon = this.el.select('i.fa-star', true).first();
32758
32759         if(label && icon){
32760             icon.remove();
32761         }
32762         
32763         this.fireEvent('valid', this);
32764     },
32765     
32766      /**
32767      * Mark this field as invalid
32768      * @param {String} msg The validation message
32769      */
32770     markInvalid : function(msg)
32771     {
32772         
32773         var label = this.el.select('label', true).first();
32774         var icon = this.el.select('i.fa-star', true).first();
32775
32776         if(label && !icon){
32777             this.el.select('.roo-date-split-field-label', true).createChild({
32778                 tag : 'i',
32779                 cls : 'text-danger fa fa-lg fa-star',
32780                 tooltip : 'This field is required',
32781                 style : 'margin-right:5px;'
32782             }, label, true);
32783         }
32784         
32785         this.fireEvent('invalid', this, msg);
32786     },
32787     
32788     clearInvalid : function()
32789     {
32790         var label = this.el.select('label', true).first();
32791         var icon = this.el.select('i.fa-star', true).first();
32792
32793         if(label && icon){
32794             icon.remove();
32795         }
32796         
32797         this.fireEvent('valid', this);
32798     },
32799     
32800     getName: function()
32801     {
32802         return this.name;
32803     }
32804     
32805 });
32806
32807  /**
32808  *
32809  * This is based on 
32810  * http://masonry.desandro.com
32811  *
32812  * The idea is to render all the bricks based on vertical width...
32813  *
32814  * The original code extends 'outlayer' - we might need to use that....
32815  * 
32816  */
32817
32818
32819 /**
32820  * @class Roo.bootstrap.LayoutMasonry
32821  * @extends Roo.bootstrap.Component
32822  * Bootstrap Layout Masonry class
32823  * 
32824  * @constructor
32825  * Create a new Element
32826  * @param {Object} config The config object
32827  */
32828
32829 Roo.bootstrap.LayoutMasonry = function(config){
32830     
32831     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32832     
32833     this.bricks = [];
32834     
32835     Roo.bootstrap.LayoutMasonry.register(this);
32836     
32837     this.addEvents({
32838         // raw events
32839         /**
32840          * @event layout
32841          * Fire after layout the items
32842          * @param {Roo.bootstrap.LayoutMasonry} this
32843          * @param {Roo.EventObject} e
32844          */
32845         "layout" : true
32846     });
32847     
32848 };
32849
32850 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
32851     
32852     /**
32853      * @cfg {Boolean} isLayoutInstant = no animation?
32854      */   
32855     isLayoutInstant : false, // needed?
32856    
32857     /**
32858      * @cfg {Number} boxWidth  width of the columns
32859      */   
32860     boxWidth : 450,
32861     
32862       /**
32863      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
32864      */   
32865     boxHeight : 0,
32866     
32867     /**
32868      * @cfg {Number} padWidth padding below box..
32869      */   
32870     padWidth : 10, 
32871     
32872     /**
32873      * @cfg {Number} gutter gutter width..
32874      */   
32875     gutter : 10,
32876     
32877      /**
32878      * @cfg {Number} maxCols maximum number of columns
32879      */   
32880     
32881     maxCols: 0,
32882     
32883     /**
32884      * @cfg {Boolean} isAutoInitial defalut true
32885      */   
32886     isAutoInitial : true, 
32887     
32888     containerWidth: 0,
32889     
32890     /**
32891      * @cfg {Boolean} isHorizontal defalut false
32892      */   
32893     isHorizontal : false, 
32894
32895     currentSize : null,
32896     
32897     tag: 'div',
32898     
32899     cls: '',
32900     
32901     bricks: null, //CompositeElement
32902     
32903     cols : 1,
32904     
32905     _isLayoutInited : false,
32906     
32907 //    isAlternative : false, // only use for vertical layout...
32908     
32909     /**
32910      * @cfg {Number} alternativePadWidth padding below box..
32911      */   
32912     alternativePadWidth : 50,
32913     
32914     selectedBrick : [],
32915     
32916     getAutoCreate : function(){
32917         
32918         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
32919         
32920         var cfg = {
32921             tag: this.tag,
32922             cls: 'blog-masonary-wrapper ' + this.cls,
32923             cn : {
32924                 cls : 'mas-boxes masonary'
32925             }
32926         };
32927         
32928         return cfg;
32929     },
32930     
32931     getChildContainer: function( )
32932     {
32933         if (this.boxesEl) {
32934             return this.boxesEl;
32935         }
32936         
32937         this.boxesEl = this.el.select('.mas-boxes').first();
32938         
32939         return this.boxesEl;
32940     },
32941     
32942     
32943     initEvents : function()
32944     {
32945         var _this = this;
32946         
32947         if(this.isAutoInitial){
32948             Roo.log('hook children rendered');
32949             this.on('childrenrendered', function() {
32950                 Roo.log('children rendered');
32951                 _this.initial();
32952             } ,this);
32953         }
32954     },
32955     
32956     initial : function()
32957     {
32958         this.selectedBrick = [];
32959         
32960         this.currentSize = this.el.getBox(true);
32961         
32962         Roo.EventManager.onWindowResize(this.resize, this); 
32963
32964         if(!this.isAutoInitial){
32965             this.layout();
32966             return;
32967         }
32968         
32969         this.layout();
32970         
32971         return;
32972         //this.layout.defer(500,this);
32973         
32974     },
32975     
32976     resize : function()
32977     {
32978         var cs = this.el.getBox(true);
32979         
32980         if (
32981                 this.currentSize.width == cs.width && 
32982                 this.currentSize.x == cs.x && 
32983                 this.currentSize.height == cs.height && 
32984                 this.currentSize.y == cs.y 
32985         ) {
32986             Roo.log("no change in with or X or Y");
32987             return;
32988         }
32989         
32990         this.currentSize = cs;
32991         
32992         this.layout();
32993         
32994     },
32995     
32996     layout : function()
32997     {   
32998         this._resetLayout();
32999         
33000         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33001         
33002         this.layoutItems( isInstant );
33003       
33004         this._isLayoutInited = true;
33005         
33006         this.fireEvent('layout', this);
33007         
33008     },
33009     
33010     _resetLayout : function()
33011     {
33012         if(this.isHorizontal){
33013             this.horizontalMeasureColumns();
33014             return;
33015         }
33016         
33017         this.verticalMeasureColumns();
33018         
33019     },
33020     
33021     verticalMeasureColumns : function()
33022     {
33023         this.getContainerWidth();
33024         
33025 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33026 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33027 //            return;
33028 //        }
33029         
33030         var boxWidth = this.boxWidth + this.padWidth;
33031         
33032         if(this.containerWidth < this.boxWidth){
33033             boxWidth = this.containerWidth
33034         }
33035         
33036         var containerWidth = this.containerWidth;
33037         
33038         var cols = Math.floor(containerWidth / boxWidth);
33039         
33040         this.cols = Math.max( cols, 1 );
33041         
33042         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33043         
33044         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33045         
33046         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33047         
33048         this.colWidth = boxWidth + avail - this.padWidth;
33049         
33050         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33051         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33052     },
33053     
33054     horizontalMeasureColumns : function()
33055     {
33056         this.getContainerWidth();
33057         
33058         var boxWidth = this.boxWidth;
33059         
33060         if(this.containerWidth < boxWidth){
33061             boxWidth = this.containerWidth;
33062         }
33063         
33064         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33065         
33066         this.el.setHeight(boxWidth);
33067         
33068     },
33069     
33070     getContainerWidth : function()
33071     {
33072         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33073     },
33074     
33075     layoutItems : function( isInstant )
33076     {
33077         Roo.log(this.bricks);
33078         
33079         var items = Roo.apply([], this.bricks);
33080         
33081         if(this.isHorizontal){
33082             this._horizontalLayoutItems( items , isInstant );
33083             return;
33084         }
33085         
33086 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33087 //            this._verticalAlternativeLayoutItems( items , isInstant );
33088 //            return;
33089 //        }
33090         
33091         this._verticalLayoutItems( items , isInstant );
33092         
33093     },
33094     
33095     _verticalLayoutItems : function ( items , isInstant)
33096     {
33097         if ( !items || !items.length ) {
33098             return;
33099         }
33100         
33101         var standard = [
33102             ['xs', 'xs', 'xs', 'tall'],
33103             ['xs', 'xs', 'tall'],
33104             ['xs', 'xs', 'sm'],
33105             ['xs', 'xs', 'xs'],
33106             ['xs', 'tall'],
33107             ['xs', 'sm'],
33108             ['xs', 'xs'],
33109             ['xs'],
33110             
33111             ['sm', 'xs', 'xs'],
33112             ['sm', 'xs'],
33113             ['sm'],
33114             
33115             ['tall', 'xs', 'xs', 'xs'],
33116             ['tall', 'xs', 'xs'],
33117             ['tall', 'xs'],
33118             ['tall']
33119             
33120         ];
33121         
33122         var queue = [];
33123         
33124         var boxes = [];
33125         
33126         var box = [];
33127         
33128         Roo.each(items, function(item, k){
33129             
33130             switch (item.size) {
33131                 // these layouts take up a full box,
33132                 case 'md' :
33133                 case 'md-left' :
33134                 case 'md-right' :
33135                 case 'wide' :
33136                     
33137                     if(box.length){
33138                         boxes.push(box);
33139                         box = [];
33140                     }
33141                     
33142                     boxes.push([item]);
33143                     
33144                     break;
33145                     
33146                 case 'xs' :
33147                 case 'sm' :
33148                 case 'tall' :
33149                     
33150                     box.push(item);
33151                     
33152                     break;
33153                 default :
33154                     break;
33155                     
33156             }
33157             
33158         }, this);
33159         
33160         if(box.length){
33161             boxes.push(box);
33162             box = [];
33163         }
33164         
33165         var filterPattern = function(box, length)
33166         {
33167             if(!box.length){
33168                 return;
33169             }
33170             
33171             var match = false;
33172             
33173             var pattern = box.slice(0, length);
33174             
33175             var format = [];
33176             
33177             Roo.each(pattern, function(i){
33178                 format.push(i.size);
33179             }, this);
33180             
33181             Roo.each(standard, function(s){
33182                 
33183                 if(String(s) != String(format)){
33184                     return;
33185                 }
33186                 
33187                 match = true;
33188                 return false;
33189                 
33190             }, this);
33191             
33192             if(!match && length == 1){
33193                 return;
33194             }
33195             
33196             if(!match){
33197                 filterPattern(box, length - 1);
33198                 return;
33199             }
33200                 
33201             queue.push(pattern);
33202
33203             box = box.slice(length, box.length);
33204
33205             filterPattern(box, 4);
33206
33207             return;
33208             
33209         }
33210         
33211         Roo.each(boxes, function(box, k){
33212             
33213             if(!box.length){
33214                 return;
33215             }
33216             
33217             if(box.length == 1){
33218                 queue.push(box);
33219                 return;
33220             }
33221             
33222             filterPattern(box, 4);
33223             
33224         }, this);
33225         
33226         this._processVerticalLayoutQueue( queue, isInstant );
33227         
33228     },
33229     
33230 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33231 //    {
33232 //        if ( !items || !items.length ) {
33233 //            return;
33234 //        }
33235 //
33236 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33237 //        
33238 //    },
33239     
33240     _horizontalLayoutItems : function ( items , isInstant)
33241     {
33242         if ( !items || !items.length || items.length < 3) {
33243             return;
33244         }
33245         
33246         items.reverse();
33247         
33248         var eItems = items.slice(0, 3);
33249         
33250         items = items.slice(3, items.length);
33251         
33252         var standard = [
33253             ['xs', 'xs', 'xs', 'wide'],
33254             ['xs', 'xs', 'wide'],
33255             ['xs', 'xs', 'sm'],
33256             ['xs', 'xs', 'xs'],
33257             ['xs', 'wide'],
33258             ['xs', 'sm'],
33259             ['xs', 'xs'],
33260             ['xs'],
33261             
33262             ['sm', 'xs', 'xs'],
33263             ['sm', 'xs'],
33264             ['sm'],
33265             
33266             ['wide', 'xs', 'xs', 'xs'],
33267             ['wide', 'xs', 'xs'],
33268             ['wide', 'xs'],
33269             ['wide'],
33270             
33271             ['wide-thin']
33272         ];
33273         
33274         var queue = [];
33275         
33276         var boxes = [];
33277         
33278         var box = [];
33279         
33280         Roo.each(items, function(item, k){
33281             
33282             switch (item.size) {
33283                 case 'md' :
33284                 case 'md-left' :
33285                 case 'md-right' :
33286                 case 'tall' :
33287                     
33288                     if(box.length){
33289                         boxes.push(box);
33290                         box = [];
33291                     }
33292                     
33293                     boxes.push([item]);
33294                     
33295                     break;
33296                     
33297                 case 'xs' :
33298                 case 'sm' :
33299                 case 'wide' :
33300                 case 'wide-thin' :
33301                     
33302                     box.push(item);
33303                     
33304                     break;
33305                 default :
33306                     break;
33307                     
33308             }
33309             
33310         }, this);
33311         
33312         if(box.length){
33313             boxes.push(box);
33314             box = [];
33315         }
33316         
33317         var filterPattern = function(box, length)
33318         {
33319             if(!box.length){
33320                 return;
33321             }
33322             
33323             var match = false;
33324             
33325             var pattern = box.slice(0, length);
33326             
33327             var format = [];
33328             
33329             Roo.each(pattern, function(i){
33330                 format.push(i.size);
33331             }, this);
33332             
33333             Roo.each(standard, function(s){
33334                 
33335                 if(String(s) != String(format)){
33336                     return;
33337                 }
33338                 
33339                 match = true;
33340                 return false;
33341                 
33342             }, this);
33343             
33344             if(!match && length == 1){
33345                 return;
33346             }
33347             
33348             if(!match){
33349                 filterPattern(box, length - 1);
33350                 return;
33351             }
33352                 
33353             queue.push(pattern);
33354
33355             box = box.slice(length, box.length);
33356
33357             filterPattern(box, 4);
33358
33359             return;
33360             
33361         }
33362         
33363         Roo.each(boxes, function(box, k){
33364             
33365             if(!box.length){
33366                 return;
33367             }
33368             
33369             if(box.length == 1){
33370                 queue.push(box);
33371                 return;
33372             }
33373             
33374             filterPattern(box, 4);
33375             
33376         }, this);
33377         
33378         
33379         var prune = [];
33380         
33381         var pos = this.el.getBox(true);
33382         
33383         var minX = pos.x;
33384         
33385         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33386         
33387         var hit_end = false;
33388         
33389         Roo.each(queue, function(box){
33390             
33391             if(hit_end){
33392                 
33393                 Roo.each(box, function(b){
33394                 
33395                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33396                     b.el.hide();
33397
33398                 }, this);
33399
33400                 return;
33401             }
33402             
33403             var mx = 0;
33404             
33405             Roo.each(box, function(b){
33406                 
33407                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33408                 b.el.show();
33409
33410                 mx = Math.max(mx, b.x);
33411                 
33412             }, this);
33413             
33414             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33415             
33416             if(maxX < minX){
33417                 
33418                 Roo.each(box, function(b){
33419                 
33420                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33421                     b.el.hide();
33422                     
33423                 }, this);
33424                 
33425                 hit_end = true;
33426                 
33427                 return;
33428             }
33429             
33430             prune.push(box);
33431             
33432         }, this);
33433         
33434         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33435     },
33436     
33437     /** Sets position of item in DOM
33438     * @param {Element} item
33439     * @param {Number} x - horizontal position
33440     * @param {Number} y - vertical position
33441     * @param {Boolean} isInstant - disables transitions
33442     */
33443     _processVerticalLayoutQueue : function( queue, isInstant )
33444     {
33445         var pos = this.el.getBox(true);
33446         var x = pos.x;
33447         var y = pos.y;
33448         var maxY = [];
33449         
33450         for (var i = 0; i < this.cols; i++){
33451             maxY[i] = pos.y;
33452         }
33453         
33454         Roo.each(queue, function(box, k){
33455             
33456             var col = k % this.cols;
33457             
33458             Roo.each(box, function(b,kk){
33459                 
33460                 b.el.position('absolute');
33461                 
33462                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33463                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33464                 
33465                 if(b.size == 'md-left' || b.size == 'md-right'){
33466                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33467                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33468                 }
33469                 
33470                 b.el.setWidth(width);
33471                 b.el.setHeight(height);
33472                 // iframe?
33473                 b.el.select('iframe',true).setSize(width,height);
33474                 
33475             }, this);
33476             
33477             for (var i = 0; i < this.cols; i++){
33478                 
33479                 if(maxY[i] < maxY[col]){
33480                     col = i;
33481                     continue;
33482                 }
33483                 
33484                 col = Math.min(col, i);
33485                 
33486             }
33487             
33488             x = pos.x + col * (this.colWidth + this.padWidth);
33489             
33490             y = maxY[col];
33491             
33492             var positions = [];
33493             
33494             switch (box.length){
33495                 case 1 :
33496                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33497                     break;
33498                 case 2 :
33499                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33500                     break;
33501                 case 3 :
33502                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33503                     break;
33504                 case 4 :
33505                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33506                     break;
33507                 default :
33508                     break;
33509             }
33510             
33511             Roo.each(box, function(b,kk){
33512                 
33513                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33514                 
33515                 var sz = b.el.getSize();
33516                 
33517                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33518                 
33519             }, this);
33520             
33521         }, this);
33522         
33523         var mY = 0;
33524         
33525         for (var i = 0; i < this.cols; i++){
33526             mY = Math.max(mY, maxY[i]);
33527         }
33528         
33529         this.el.setHeight(mY - pos.y);
33530         
33531     },
33532     
33533 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33534 //    {
33535 //        var pos = this.el.getBox(true);
33536 //        var x = pos.x;
33537 //        var y = pos.y;
33538 //        var maxX = pos.right;
33539 //        
33540 //        var maxHeight = 0;
33541 //        
33542 //        Roo.each(items, function(item, k){
33543 //            
33544 //            var c = k % 2;
33545 //            
33546 //            item.el.position('absolute');
33547 //                
33548 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33549 //
33550 //            item.el.setWidth(width);
33551 //
33552 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33553 //
33554 //            item.el.setHeight(height);
33555 //            
33556 //            if(c == 0){
33557 //                item.el.setXY([x, y], isInstant ? false : true);
33558 //            } else {
33559 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33560 //            }
33561 //            
33562 //            y = y + height + this.alternativePadWidth;
33563 //            
33564 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33565 //            
33566 //        }, this);
33567 //        
33568 //        this.el.setHeight(maxHeight);
33569 //        
33570 //    },
33571     
33572     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33573     {
33574         var pos = this.el.getBox(true);
33575         
33576         var minX = pos.x;
33577         var minY = pos.y;
33578         
33579         var maxX = pos.right;
33580         
33581         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33582         
33583         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33584         
33585         Roo.each(queue, function(box, k){
33586             
33587             Roo.each(box, function(b, kk){
33588                 
33589                 b.el.position('absolute');
33590                 
33591                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33592                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33593                 
33594                 if(b.size == 'md-left' || b.size == 'md-right'){
33595                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33596                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33597                 }
33598                 
33599                 b.el.setWidth(width);
33600                 b.el.setHeight(height);
33601                 
33602             }, this);
33603             
33604             if(!box.length){
33605                 return;
33606             }
33607             
33608             var positions = [];
33609             
33610             switch (box.length){
33611                 case 1 :
33612                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33613                     break;
33614                 case 2 :
33615                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33616                     break;
33617                 case 3 :
33618                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33619                     break;
33620                 case 4 :
33621                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33622                     break;
33623                 default :
33624                     break;
33625             }
33626             
33627             Roo.each(box, function(b,kk){
33628                 
33629                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33630                 
33631                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33632                 
33633             }, this);
33634             
33635         }, this);
33636         
33637     },
33638     
33639     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33640     {
33641         Roo.each(eItems, function(b,k){
33642             
33643             b.size = (k == 0) ? 'sm' : 'xs';
33644             b.x = (k == 0) ? 2 : 1;
33645             b.y = (k == 0) ? 2 : 1;
33646             
33647             b.el.position('absolute');
33648             
33649             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33650                 
33651             b.el.setWidth(width);
33652             
33653             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33654             
33655             b.el.setHeight(height);
33656             
33657         }, this);
33658
33659         var positions = [];
33660         
33661         positions.push({
33662             x : maxX - this.unitWidth * 2 - this.gutter,
33663             y : minY
33664         });
33665         
33666         positions.push({
33667             x : maxX - this.unitWidth,
33668             y : minY + (this.unitWidth + this.gutter) * 2
33669         });
33670         
33671         positions.push({
33672             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33673             y : minY
33674         });
33675         
33676         Roo.each(eItems, function(b,k){
33677             
33678             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33679
33680         }, this);
33681         
33682     },
33683     
33684     getVerticalOneBoxColPositions : function(x, y, box)
33685     {
33686         var pos = [];
33687         
33688         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33689         
33690         if(box[0].size == 'md-left'){
33691             rand = 0;
33692         }
33693         
33694         if(box[0].size == 'md-right'){
33695             rand = 1;
33696         }
33697         
33698         pos.push({
33699             x : x + (this.unitWidth + this.gutter) * rand,
33700             y : y
33701         });
33702         
33703         return pos;
33704     },
33705     
33706     getVerticalTwoBoxColPositions : function(x, y, box)
33707     {
33708         var pos = [];
33709         
33710         if(box[0].size == 'xs'){
33711             
33712             pos.push({
33713                 x : x,
33714                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33715             });
33716
33717             pos.push({
33718                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33719                 y : y
33720             });
33721             
33722             return pos;
33723             
33724         }
33725         
33726         pos.push({
33727             x : x,
33728             y : y
33729         });
33730
33731         pos.push({
33732             x : x + (this.unitWidth + this.gutter) * 2,
33733             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33734         });
33735         
33736         return pos;
33737         
33738     },
33739     
33740     getVerticalThreeBoxColPositions : function(x, y, box)
33741     {
33742         var pos = [];
33743         
33744         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33745             
33746             pos.push({
33747                 x : x,
33748                 y : y
33749             });
33750
33751             pos.push({
33752                 x : x + (this.unitWidth + this.gutter) * 1,
33753                 y : y
33754             });
33755             
33756             pos.push({
33757                 x : x + (this.unitWidth + this.gutter) * 2,
33758                 y : y
33759             });
33760             
33761             return pos;
33762             
33763         }
33764         
33765         if(box[0].size == 'xs' && box[1].size == 'xs'){
33766             
33767             pos.push({
33768                 x : x,
33769                 y : y
33770             });
33771
33772             pos.push({
33773                 x : x,
33774                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33775             });
33776             
33777             pos.push({
33778                 x : x + (this.unitWidth + this.gutter) * 1,
33779                 y : y
33780             });
33781             
33782             return pos;
33783             
33784         }
33785         
33786         pos.push({
33787             x : x,
33788             y : y
33789         });
33790
33791         pos.push({
33792             x : x + (this.unitWidth + this.gutter) * 2,
33793             y : y
33794         });
33795
33796         pos.push({
33797             x : x + (this.unitWidth + this.gutter) * 2,
33798             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33799         });
33800             
33801         return pos;
33802         
33803     },
33804     
33805     getVerticalFourBoxColPositions : function(x, y, box)
33806     {
33807         var pos = [];
33808         
33809         if(box[0].size == 'xs'){
33810             
33811             pos.push({
33812                 x : x,
33813                 y : y
33814             });
33815
33816             pos.push({
33817                 x : x,
33818                 y : y + (this.unitHeight + this.gutter) * 1
33819             });
33820             
33821             pos.push({
33822                 x : x,
33823                 y : y + (this.unitHeight + this.gutter) * 2
33824             });
33825             
33826             pos.push({
33827                 x : x + (this.unitWidth + this.gutter) * 1,
33828                 y : y
33829             });
33830             
33831             return pos;
33832             
33833         }
33834         
33835         pos.push({
33836             x : x,
33837             y : y
33838         });
33839
33840         pos.push({
33841             x : x + (this.unitWidth + this.gutter) * 2,
33842             y : y
33843         });
33844
33845         pos.push({
33846             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
33847             y : y + (this.unitHeight + this.gutter) * 1
33848         });
33849
33850         pos.push({
33851             x : x + (this.unitWidth + this.gutter) * 2,
33852             y : y + (this.unitWidth + this.gutter) * 2
33853         });
33854
33855         return pos;
33856         
33857     },
33858     
33859     getHorizontalOneBoxColPositions : function(maxX, minY, box)
33860     {
33861         var pos = [];
33862         
33863         if(box[0].size == 'md-left'){
33864             pos.push({
33865                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33866                 y : minY
33867             });
33868             
33869             return pos;
33870         }
33871         
33872         if(box[0].size == 'md-right'){
33873             pos.push({
33874                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
33875                 y : minY + (this.unitWidth + this.gutter) * 1
33876             });
33877             
33878             return pos;
33879         }
33880         
33881         var rand = Math.floor(Math.random() * (4 - box[0].y));
33882         
33883         pos.push({
33884             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33885             y : minY + (this.unitWidth + this.gutter) * rand
33886         });
33887         
33888         return pos;
33889         
33890     },
33891     
33892     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
33893     {
33894         var pos = [];
33895         
33896         if(box[0].size == 'xs'){
33897             
33898             pos.push({
33899                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33900                 y : minY
33901             });
33902
33903             pos.push({
33904                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33905                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
33906             });
33907             
33908             return pos;
33909             
33910         }
33911         
33912         pos.push({
33913             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33914             y : minY
33915         });
33916
33917         pos.push({
33918             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33919             y : minY + (this.unitWidth + this.gutter) * 2
33920         });
33921         
33922         return pos;
33923         
33924     },
33925     
33926     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
33927     {
33928         var pos = [];
33929         
33930         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33931             
33932             pos.push({
33933                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33934                 y : minY
33935             });
33936
33937             pos.push({
33938                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33939                 y : minY + (this.unitWidth + this.gutter) * 1
33940             });
33941             
33942             pos.push({
33943                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33944                 y : minY + (this.unitWidth + this.gutter) * 2
33945             });
33946             
33947             return pos;
33948             
33949         }
33950         
33951         if(box[0].size == 'xs' && box[1].size == 'xs'){
33952             
33953             pos.push({
33954                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33955                 y : minY
33956             });
33957
33958             pos.push({
33959                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33960                 y : minY
33961             });
33962             
33963             pos.push({
33964                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33965                 y : minY + (this.unitWidth + this.gutter) * 1
33966             });
33967             
33968             return pos;
33969             
33970         }
33971         
33972         pos.push({
33973             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33974             y : minY
33975         });
33976
33977         pos.push({
33978             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
33979             y : minY + (this.unitWidth + this.gutter) * 2
33980         });
33981
33982         pos.push({
33983             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
33984             y : minY + (this.unitWidth + this.gutter) * 2
33985         });
33986             
33987         return pos;
33988         
33989     },
33990     
33991     getHorizontalFourBoxColPositions : function(maxX, minY, box)
33992     {
33993         var pos = [];
33994         
33995         if(box[0].size == 'xs'){
33996             
33997             pos.push({
33998                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
33999                 y : minY
34000             });
34001
34002             pos.push({
34003                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34004                 y : minY
34005             });
34006             
34007             pos.push({
34008                 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),
34009                 y : minY
34010             });
34011             
34012             pos.push({
34013                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34014                 y : minY + (this.unitWidth + this.gutter) * 1
34015             });
34016             
34017             return pos;
34018             
34019         }
34020         
34021         pos.push({
34022             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34023             y : minY
34024         });
34025         
34026         pos.push({
34027             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34028             y : minY + (this.unitWidth + this.gutter) * 2
34029         });
34030         
34031         pos.push({
34032             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34033             y : minY + (this.unitWidth + this.gutter) * 2
34034         });
34035         
34036         pos.push({
34037             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),
34038             y : minY + (this.unitWidth + this.gutter) * 2
34039         });
34040
34041         return pos;
34042         
34043     },
34044     
34045     /**
34046     * remove a Masonry Brick
34047     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34048     */
34049     removeBrick : function(brick_id)
34050     {
34051         if (!brick_id) {
34052             return;
34053         }
34054         
34055         for (var i = 0; i<this.bricks.length; i++) {
34056             if (this.bricks[i].id == brick_id) {
34057                 this.bricks.splice(i,1);
34058                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34059                 this.initial();
34060             }
34061         }
34062     },
34063     
34064     /**
34065     * adds a Masonry Brick
34066     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34067     */
34068     addBrick : function(cfg)
34069     {
34070         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34071         //this.register(cn);
34072         cn.parentId = this.id;
34073         cn.render(this.el);
34074         return cn;
34075     },
34076     
34077     /**
34078     * register a Masonry Brick
34079     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34080     */
34081     
34082     register : function(brick)
34083     {
34084         this.bricks.push(brick);
34085         brick.masonryId = this.id;
34086     },
34087     
34088     /**
34089     * clear all the Masonry Brick
34090     */
34091     clearAll : function()
34092     {
34093         this.bricks = [];
34094         //this.getChildContainer().dom.innerHTML = "";
34095         this.el.dom.innerHTML = '';
34096     },
34097     
34098     getSelected : function()
34099     {
34100         if (!this.selectedBrick) {
34101             return false;
34102         }
34103         
34104         return this.selectedBrick;
34105     }
34106 });
34107
34108 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34109     
34110     groups: {},
34111      /**
34112     * register a Masonry Layout
34113     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34114     */
34115     
34116     register : function(layout)
34117     {
34118         this.groups[layout.id] = layout;
34119     },
34120     /**
34121     * fetch a  Masonry Layout based on the masonry layout ID
34122     * @param {string} the masonry layout to add
34123     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34124     */
34125     
34126     get: function(layout_id) {
34127         if (typeof(this.groups[layout_id]) == 'undefined') {
34128             return false;
34129         }
34130         return this.groups[layout_id] ;
34131     }
34132     
34133     
34134     
34135 });
34136
34137  
34138
34139  /**
34140  *
34141  * This is based on 
34142  * http://masonry.desandro.com
34143  *
34144  * The idea is to render all the bricks based on vertical width...
34145  *
34146  * The original code extends 'outlayer' - we might need to use that....
34147  * 
34148  */
34149
34150
34151 /**
34152  * @class Roo.bootstrap.LayoutMasonryAuto
34153  * @extends Roo.bootstrap.Component
34154  * Bootstrap Layout Masonry class
34155  * 
34156  * @constructor
34157  * Create a new Element
34158  * @param {Object} config The config object
34159  */
34160
34161 Roo.bootstrap.LayoutMasonryAuto = function(config){
34162     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34163 };
34164
34165 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34166     
34167       /**
34168      * @cfg {Boolean} isFitWidth  - resize the width..
34169      */   
34170     isFitWidth : false,  // options..
34171     /**
34172      * @cfg {Boolean} isOriginLeft = left align?
34173      */   
34174     isOriginLeft : true,
34175     /**
34176      * @cfg {Boolean} isOriginTop = top align?
34177      */   
34178     isOriginTop : false,
34179     /**
34180      * @cfg {Boolean} isLayoutInstant = no animation?
34181      */   
34182     isLayoutInstant : false, // needed?
34183     /**
34184      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34185      */   
34186     isResizingContainer : true,
34187     /**
34188      * @cfg {Number} columnWidth  width of the columns 
34189      */   
34190     
34191     columnWidth : 0,
34192     
34193     /**
34194      * @cfg {Number} maxCols maximum number of columns
34195      */   
34196     
34197     maxCols: 0,
34198     /**
34199      * @cfg {Number} padHeight padding below box..
34200      */   
34201     
34202     padHeight : 10, 
34203     
34204     /**
34205      * @cfg {Boolean} isAutoInitial defalut true
34206      */   
34207     
34208     isAutoInitial : true, 
34209     
34210     // private?
34211     gutter : 0,
34212     
34213     containerWidth: 0,
34214     initialColumnWidth : 0,
34215     currentSize : null,
34216     
34217     colYs : null, // array.
34218     maxY : 0,
34219     padWidth: 10,
34220     
34221     
34222     tag: 'div',
34223     cls: '',
34224     bricks: null, //CompositeElement
34225     cols : 0, // array?
34226     // element : null, // wrapped now this.el
34227     _isLayoutInited : null, 
34228     
34229     
34230     getAutoCreate : function(){
34231         
34232         var cfg = {
34233             tag: this.tag,
34234             cls: 'blog-masonary-wrapper ' + this.cls,
34235             cn : {
34236                 cls : 'mas-boxes masonary'
34237             }
34238         };
34239         
34240         return cfg;
34241     },
34242     
34243     getChildContainer: function( )
34244     {
34245         if (this.boxesEl) {
34246             return this.boxesEl;
34247         }
34248         
34249         this.boxesEl = this.el.select('.mas-boxes').first();
34250         
34251         return this.boxesEl;
34252     },
34253     
34254     
34255     initEvents : function()
34256     {
34257         var _this = this;
34258         
34259         if(this.isAutoInitial){
34260             Roo.log('hook children rendered');
34261             this.on('childrenrendered', function() {
34262                 Roo.log('children rendered');
34263                 _this.initial();
34264             } ,this);
34265         }
34266         
34267     },
34268     
34269     initial : function()
34270     {
34271         this.reloadItems();
34272
34273         this.currentSize = this.el.getBox(true);
34274
34275         /// was window resize... - let's see if this works..
34276         Roo.EventManager.onWindowResize(this.resize, this); 
34277
34278         if(!this.isAutoInitial){
34279             this.layout();
34280             return;
34281         }
34282         
34283         this.layout.defer(500,this);
34284     },
34285     
34286     reloadItems: function()
34287     {
34288         this.bricks = this.el.select('.masonry-brick', true);
34289         
34290         this.bricks.each(function(b) {
34291             //Roo.log(b.getSize());
34292             if (!b.attr('originalwidth')) {
34293                 b.attr('originalwidth',  b.getSize().width);
34294             }
34295             
34296         });
34297         
34298         Roo.log(this.bricks.elements.length);
34299     },
34300     
34301     resize : function()
34302     {
34303         Roo.log('resize');
34304         var cs = this.el.getBox(true);
34305         
34306         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34307             Roo.log("no change in with or X");
34308             return;
34309         }
34310         this.currentSize = cs;
34311         this.layout();
34312     },
34313     
34314     layout : function()
34315     {
34316          Roo.log('layout');
34317         this._resetLayout();
34318         //this._manageStamps();
34319       
34320         // don't animate first layout
34321         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34322         this.layoutItems( isInstant );
34323       
34324         // flag for initalized
34325         this._isLayoutInited = true;
34326     },
34327     
34328     layoutItems : function( isInstant )
34329     {
34330         //var items = this._getItemsForLayout( this.items );
34331         // original code supports filtering layout items.. we just ignore it..
34332         
34333         this._layoutItems( this.bricks , isInstant );
34334       
34335         this._postLayout();
34336     },
34337     _layoutItems : function ( items , isInstant)
34338     {
34339        //this.fireEvent( 'layout', this, items );
34340     
34341
34342         if ( !items || !items.elements.length ) {
34343           // no items, emit event with empty array
34344             return;
34345         }
34346
34347         var queue = [];
34348         items.each(function(item) {
34349             Roo.log("layout item");
34350             Roo.log(item);
34351             // get x/y object from method
34352             var position = this._getItemLayoutPosition( item );
34353             // enqueue
34354             position.item = item;
34355             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34356             queue.push( position );
34357         }, this);
34358       
34359         this._processLayoutQueue( queue );
34360     },
34361     /** Sets position of item in DOM
34362     * @param {Element} item
34363     * @param {Number} x - horizontal position
34364     * @param {Number} y - vertical position
34365     * @param {Boolean} isInstant - disables transitions
34366     */
34367     _processLayoutQueue : function( queue )
34368     {
34369         for ( var i=0, len = queue.length; i < len; i++ ) {
34370             var obj = queue[i];
34371             obj.item.position('absolute');
34372             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34373         }
34374     },
34375       
34376     
34377     /**
34378     * Any logic you want to do after each layout,
34379     * i.e. size the container
34380     */
34381     _postLayout : function()
34382     {
34383         this.resizeContainer();
34384     },
34385     
34386     resizeContainer : function()
34387     {
34388         if ( !this.isResizingContainer ) {
34389             return;
34390         }
34391         var size = this._getContainerSize();
34392         if ( size ) {
34393             this.el.setSize(size.width,size.height);
34394             this.boxesEl.setSize(size.width,size.height);
34395         }
34396     },
34397     
34398     
34399     
34400     _resetLayout : function()
34401     {
34402         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34403         this.colWidth = this.el.getWidth();
34404         //this.gutter = this.el.getWidth(); 
34405         
34406         this.measureColumns();
34407
34408         // reset column Y
34409         var i = this.cols;
34410         this.colYs = [];
34411         while (i--) {
34412             this.colYs.push( 0 );
34413         }
34414     
34415         this.maxY = 0;
34416     },
34417
34418     measureColumns : function()
34419     {
34420         this.getContainerWidth();
34421       // if columnWidth is 0, default to outerWidth of first item
34422         if ( !this.columnWidth ) {
34423             var firstItem = this.bricks.first();
34424             Roo.log(firstItem);
34425             this.columnWidth  = this.containerWidth;
34426             if (firstItem && firstItem.attr('originalwidth') ) {
34427                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34428             }
34429             // columnWidth fall back to item of first element
34430             Roo.log("set column width?");
34431                         this.initialColumnWidth = this.columnWidth  ;
34432
34433             // if first elem has no width, default to size of container
34434             
34435         }
34436         
34437         
34438         if (this.initialColumnWidth) {
34439             this.columnWidth = this.initialColumnWidth;
34440         }
34441         
34442         
34443             
34444         // column width is fixed at the top - however if container width get's smaller we should
34445         // reduce it...
34446         
34447         // this bit calcs how man columns..
34448             
34449         var columnWidth = this.columnWidth += this.gutter;
34450       
34451         // calculate columns
34452         var containerWidth = this.containerWidth + this.gutter;
34453         
34454         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34455         // fix rounding errors, typically with gutters
34456         var excess = columnWidth - containerWidth % columnWidth;
34457         
34458         
34459         // if overshoot is less than a pixel, round up, otherwise floor it
34460         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34461         cols = Math[ mathMethod ]( cols );
34462         this.cols = Math.max( cols, 1 );
34463         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34464         
34465          // padding positioning..
34466         var totalColWidth = this.cols * this.columnWidth;
34467         var padavail = this.containerWidth - totalColWidth;
34468         // so for 2 columns - we need 3 'pads'
34469         
34470         var padNeeded = (1+this.cols) * this.padWidth;
34471         
34472         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34473         
34474         this.columnWidth += padExtra
34475         //this.padWidth = Math.floor(padavail /  ( this.cols));
34476         
34477         // adjust colum width so that padding is fixed??
34478         
34479         // we have 3 columns ... total = width * 3
34480         // we have X left over... that should be used by 
34481         
34482         //if (this.expandC) {
34483             
34484         //}
34485         
34486         
34487         
34488     },
34489     
34490     getContainerWidth : function()
34491     {
34492        /* // container is parent if fit width
34493         var container = this.isFitWidth ? this.element.parentNode : this.element;
34494         // check that this.size and size are there
34495         // IE8 triggers resize on body size change, so they might not be
34496         
34497         var size = getSize( container );  //FIXME
34498         this.containerWidth = size && size.innerWidth; //FIXME
34499         */
34500          
34501         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34502         
34503     },
34504     
34505     _getItemLayoutPosition : function( item )  // what is item?
34506     {
34507         // we resize the item to our columnWidth..
34508       
34509         item.setWidth(this.columnWidth);
34510         item.autoBoxAdjust  = false;
34511         
34512         var sz = item.getSize();
34513  
34514         // how many columns does this brick span
34515         var remainder = this.containerWidth % this.columnWidth;
34516         
34517         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34518         // round if off by 1 pixel, otherwise use ceil
34519         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34520         colSpan = Math.min( colSpan, this.cols );
34521         
34522         // normally this should be '1' as we dont' currently allow multi width columns..
34523         
34524         var colGroup = this._getColGroup( colSpan );
34525         // get the minimum Y value from the columns
34526         var minimumY = Math.min.apply( Math, colGroup );
34527         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34528         
34529         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34530          
34531         // position the brick
34532         var position = {
34533             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34534             y: this.currentSize.y + minimumY + this.padHeight
34535         };
34536         
34537         Roo.log(position);
34538         // apply setHeight to necessary columns
34539         var setHeight = minimumY + sz.height + this.padHeight;
34540         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34541         
34542         var setSpan = this.cols + 1 - colGroup.length;
34543         for ( var i = 0; i < setSpan; i++ ) {
34544           this.colYs[ shortColIndex + i ] = setHeight ;
34545         }
34546       
34547         return position;
34548     },
34549     
34550     /**
34551      * @param {Number} colSpan - number of columns the element spans
34552      * @returns {Array} colGroup
34553      */
34554     _getColGroup : function( colSpan )
34555     {
34556         if ( colSpan < 2 ) {
34557           // if brick spans only one column, use all the column Ys
34558           return this.colYs;
34559         }
34560       
34561         var colGroup = [];
34562         // how many different places could this brick fit horizontally
34563         var groupCount = this.cols + 1 - colSpan;
34564         // for each group potential horizontal position
34565         for ( var i = 0; i < groupCount; i++ ) {
34566           // make an array of colY values for that one group
34567           var groupColYs = this.colYs.slice( i, i + colSpan );
34568           // and get the max value of the array
34569           colGroup[i] = Math.max.apply( Math, groupColYs );
34570         }
34571         return colGroup;
34572     },
34573     /*
34574     _manageStamp : function( stamp )
34575     {
34576         var stampSize =  stamp.getSize();
34577         var offset = stamp.getBox();
34578         // get the columns that this stamp affects
34579         var firstX = this.isOriginLeft ? offset.x : offset.right;
34580         var lastX = firstX + stampSize.width;
34581         var firstCol = Math.floor( firstX / this.columnWidth );
34582         firstCol = Math.max( 0, firstCol );
34583         
34584         var lastCol = Math.floor( lastX / this.columnWidth );
34585         // lastCol should not go over if multiple of columnWidth #425
34586         lastCol -= lastX % this.columnWidth ? 0 : 1;
34587         lastCol = Math.min( this.cols - 1, lastCol );
34588         
34589         // set colYs to bottom of the stamp
34590         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34591             stampSize.height;
34592             
34593         for ( var i = firstCol; i <= lastCol; i++ ) {
34594           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34595         }
34596     },
34597     */
34598     
34599     _getContainerSize : function()
34600     {
34601         this.maxY = Math.max.apply( Math, this.colYs );
34602         var size = {
34603             height: this.maxY
34604         };
34605       
34606         if ( this.isFitWidth ) {
34607             size.width = this._getContainerFitWidth();
34608         }
34609       
34610         return size;
34611     },
34612     
34613     _getContainerFitWidth : function()
34614     {
34615         var unusedCols = 0;
34616         // count unused columns
34617         var i = this.cols;
34618         while ( --i ) {
34619           if ( this.colYs[i] !== 0 ) {
34620             break;
34621           }
34622           unusedCols++;
34623         }
34624         // fit container to columns that have been used
34625         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34626     },
34627     
34628     needsResizeLayout : function()
34629     {
34630         var previousWidth = this.containerWidth;
34631         this.getContainerWidth();
34632         return previousWidth !== this.containerWidth;
34633     }
34634  
34635 });
34636
34637  
34638
34639  /*
34640  * - LGPL
34641  *
34642  * element
34643  * 
34644  */
34645
34646 /**
34647  * @class Roo.bootstrap.MasonryBrick
34648  * @extends Roo.bootstrap.Component
34649  * Bootstrap MasonryBrick class
34650  * 
34651  * @constructor
34652  * Create a new MasonryBrick
34653  * @param {Object} config The config object
34654  */
34655
34656 Roo.bootstrap.MasonryBrick = function(config){
34657     
34658     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34659     
34660     Roo.bootstrap.MasonryBrick.register(this);
34661     
34662     this.addEvents({
34663         // raw events
34664         /**
34665          * @event click
34666          * When a MasonryBrick is clcik
34667          * @param {Roo.bootstrap.MasonryBrick} this
34668          * @param {Roo.EventObject} e
34669          */
34670         "click" : true
34671     });
34672 };
34673
34674 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34675     
34676     /**
34677      * @cfg {String} title
34678      */   
34679     title : '',
34680     /**
34681      * @cfg {String} html
34682      */   
34683     html : '',
34684     /**
34685      * @cfg {String} bgimage
34686      */   
34687     bgimage : '',
34688     /**
34689      * @cfg {String} videourl
34690      */   
34691     videourl : '',
34692     /**
34693      * @cfg {String} cls
34694      */   
34695     cls : '',
34696     /**
34697      * @cfg {String} href
34698      */   
34699     href : '',
34700     /**
34701      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34702      */   
34703     size : 'xs',
34704     
34705     /**
34706      * @cfg {String} placetitle (center|bottom)
34707      */   
34708     placetitle : '',
34709     
34710     /**
34711      * @cfg {Boolean} isFitContainer defalut true
34712      */   
34713     isFitContainer : true, 
34714     
34715     /**
34716      * @cfg {Boolean} preventDefault defalut false
34717      */   
34718     preventDefault : false, 
34719     
34720     /**
34721      * @cfg {Boolean} inverse defalut false
34722      */   
34723     maskInverse : false, 
34724     
34725     getAutoCreate : function()
34726     {
34727         if(!this.isFitContainer){
34728             return this.getSplitAutoCreate();
34729         }
34730         
34731         var cls = 'masonry-brick masonry-brick-full';
34732         
34733         if(this.href.length){
34734             cls += ' masonry-brick-link';
34735         }
34736         
34737         if(this.bgimage.length){
34738             cls += ' masonry-brick-image';
34739         }
34740         
34741         if(this.maskInverse){
34742             cls += ' mask-inverse';
34743         }
34744         
34745         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34746             cls += ' enable-mask';
34747         }
34748         
34749         if(this.size){
34750             cls += ' masonry-' + this.size + '-brick';
34751         }
34752         
34753         if(this.placetitle.length){
34754             
34755             switch (this.placetitle) {
34756                 case 'center' :
34757                     cls += ' masonry-center-title';
34758                     break;
34759                 case 'bottom' :
34760                     cls += ' masonry-bottom-title';
34761                     break;
34762                 default:
34763                     break;
34764             }
34765             
34766         } else {
34767             if(!this.html.length && !this.bgimage.length){
34768                 cls += ' masonry-center-title';
34769             }
34770
34771             if(!this.html.length && this.bgimage.length){
34772                 cls += ' masonry-bottom-title';
34773             }
34774         }
34775         
34776         if(this.cls){
34777             cls += ' ' + this.cls;
34778         }
34779         
34780         var cfg = {
34781             tag: (this.href.length) ? 'a' : 'div',
34782             cls: cls,
34783             cn: [
34784                 {
34785                     tag: 'div',
34786                     cls: 'masonry-brick-mask'
34787                 },
34788                 {
34789                     tag: 'div',
34790                     cls: 'masonry-brick-paragraph',
34791                     cn: []
34792                 }
34793             ]
34794         };
34795         
34796         if(this.href.length){
34797             cfg.href = this.href;
34798         }
34799         
34800         var cn = cfg.cn[1].cn;
34801         
34802         if(this.title.length){
34803             cn.push({
34804                 tag: 'h4',
34805                 cls: 'masonry-brick-title',
34806                 html: this.title
34807             });
34808         }
34809         
34810         if(this.html.length){
34811             cn.push({
34812                 tag: 'p',
34813                 cls: 'masonry-brick-text',
34814                 html: this.html
34815             });
34816         }
34817         
34818         if (!this.title.length && !this.html.length) {
34819             cfg.cn[1].cls += ' hide';
34820         }
34821         
34822         if(this.bgimage.length){
34823             cfg.cn.push({
34824                 tag: 'img',
34825                 cls: 'masonry-brick-image-view',
34826                 src: this.bgimage
34827             });
34828         }
34829         
34830         if(this.videourl.length){
34831             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34832             // youtube support only?
34833             cfg.cn.push({
34834                 tag: 'iframe',
34835                 cls: 'masonry-brick-image-view',
34836                 src: vurl,
34837                 frameborder : 0,
34838                 allowfullscreen : true
34839             });
34840         }
34841         
34842         return cfg;
34843         
34844     },
34845     
34846     getSplitAutoCreate : function()
34847     {
34848         var cls = 'masonry-brick masonry-brick-split';
34849         
34850         if(this.href.length){
34851             cls += ' masonry-brick-link';
34852         }
34853         
34854         if(this.bgimage.length){
34855             cls += ' masonry-brick-image';
34856         }
34857         
34858         if(this.size){
34859             cls += ' masonry-' + this.size + '-brick';
34860         }
34861         
34862         switch (this.placetitle) {
34863             case 'center' :
34864                 cls += ' masonry-center-title';
34865                 break;
34866             case 'bottom' :
34867                 cls += ' masonry-bottom-title';
34868                 break;
34869             default:
34870                 if(!this.bgimage.length){
34871                     cls += ' masonry-center-title';
34872                 }
34873
34874                 if(this.bgimage.length){
34875                     cls += ' masonry-bottom-title';
34876                 }
34877                 break;
34878         }
34879         
34880         if(this.cls){
34881             cls += ' ' + this.cls;
34882         }
34883         
34884         var cfg = {
34885             tag: (this.href.length) ? 'a' : 'div',
34886             cls: cls,
34887             cn: [
34888                 {
34889                     tag: 'div',
34890                     cls: 'masonry-brick-split-head',
34891                     cn: [
34892                         {
34893                             tag: 'div',
34894                             cls: 'masonry-brick-paragraph',
34895                             cn: []
34896                         }
34897                     ]
34898                 },
34899                 {
34900                     tag: 'div',
34901                     cls: 'masonry-brick-split-body',
34902                     cn: []
34903                 }
34904             ]
34905         };
34906         
34907         if(this.href.length){
34908             cfg.href = this.href;
34909         }
34910         
34911         if(this.title.length){
34912             cfg.cn[0].cn[0].cn.push({
34913                 tag: 'h4',
34914                 cls: 'masonry-brick-title',
34915                 html: this.title
34916             });
34917         }
34918         
34919         if(this.html.length){
34920             cfg.cn[1].cn.push({
34921                 tag: 'p',
34922                 cls: 'masonry-brick-text',
34923                 html: this.html
34924             });
34925         }
34926
34927         if(this.bgimage.length){
34928             cfg.cn[0].cn.push({
34929                 tag: 'img',
34930                 cls: 'masonry-brick-image-view',
34931                 src: this.bgimage
34932             });
34933         }
34934         
34935         if(this.videourl.length){
34936             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34937             // youtube support only?
34938             cfg.cn[0].cn.cn.push({
34939                 tag: 'iframe',
34940                 cls: 'masonry-brick-image-view',
34941                 src: vurl,
34942                 frameborder : 0,
34943                 allowfullscreen : true
34944             });
34945         }
34946         
34947         return cfg;
34948     },
34949     
34950     initEvents: function() 
34951     {
34952         switch (this.size) {
34953             case 'xs' :
34954                 this.x = 1;
34955                 this.y = 1;
34956                 break;
34957             case 'sm' :
34958                 this.x = 2;
34959                 this.y = 2;
34960                 break;
34961             case 'md' :
34962             case 'md-left' :
34963             case 'md-right' :
34964                 this.x = 3;
34965                 this.y = 3;
34966                 break;
34967             case 'tall' :
34968                 this.x = 2;
34969                 this.y = 3;
34970                 break;
34971             case 'wide' :
34972                 this.x = 3;
34973                 this.y = 2;
34974                 break;
34975             case 'wide-thin' :
34976                 this.x = 3;
34977                 this.y = 1;
34978                 break;
34979                         
34980             default :
34981                 break;
34982         }
34983         
34984         if(Roo.isTouch){
34985             this.el.on('touchstart', this.onTouchStart, this);
34986             this.el.on('touchmove', this.onTouchMove, this);
34987             this.el.on('touchend', this.onTouchEnd, this);
34988             this.el.on('contextmenu', this.onContextMenu, this);
34989         } else {
34990             this.el.on('mouseenter'  ,this.enter, this);
34991             this.el.on('mouseleave', this.leave, this);
34992             this.el.on('click', this.onClick, this);
34993         }
34994         
34995         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
34996             this.parent().bricks.push(this);   
34997         }
34998         
34999     },
35000     
35001     onClick: function(e, el)
35002     {
35003         var time = this.endTimer - this.startTimer;
35004         // Roo.log(e.preventDefault());
35005         if(Roo.isTouch){
35006             if(time > 1000){
35007                 e.preventDefault();
35008                 return;
35009             }
35010         }
35011         
35012         if(!this.preventDefault){
35013             return;
35014         }
35015         
35016         e.preventDefault();
35017         
35018         if (this.activeClass != '') {
35019             this.selectBrick();
35020         }
35021         
35022         this.fireEvent('click', this, e);
35023     },
35024     
35025     enter: function(e, el)
35026     {
35027         e.preventDefault();
35028         
35029         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35030             return;
35031         }
35032         
35033         if(this.bgimage.length && this.html.length){
35034             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35035         }
35036     },
35037     
35038     leave: function(e, el)
35039     {
35040         e.preventDefault();
35041         
35042         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35043             return;
35044         }
35045         
35046         if(this.bgimage.length && this.html.length){
35047             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35048         }
35049     },
35050     
35051     onTouchStart: function(e, el)
35052     {
35053 //        e.preventDefault();
35054         
35055         this.touchmoved = false;
35056         
35057         if(!this.isFitContainer){
35058             return;
35059         }
35060         
35061         if(!this.bgimage.length || !this.html.length){
35062             return;
35063         }
35064         
35065         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35066         
35067         this.timer = new Date().getTime();
35068         
35069     },
35070     
35071     onTouchMove: function(e, el)
35072     {
35073         this.touchmoved = true;
35074     },
35075     
35076     onContextMenu : function(e,el)
35077     {
35078         e.preventDefault();
35079         e.stopPropagation();
35080         return false;
35081     },
35082     
35083     onTouchEnd: function(e, el)
35084     {
35085 //        e.preventDefault();
35086         
35087         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35088         
35089             this.leave(e,el);
35090             
35091             return;
35092         }
35093         
35094         if(!this.bgimage.length || !this.html.length){
35095             
35096             if(this.href.length){
35097                 window.location.href = this.href;
35098             }
35099             
35100             return;
35101         }
35102         
35103         if(!this.isFitContainer){
35104             return;
35105         }
35106         
35107         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35108         
35109         window.location.href = this.href;
35110     },
35111     
35112     //selection on single brick only
35113     selectBrick : function() {
35114         
35115         if (!this.parentId) {
35116             return;
35117         }
35118         
35119         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35120         var index = m.selectedBrick.indexOf(this.id);
35121         
35122         if ( index > -1) {
35123             m.selectedBrick.splice(index,1);
35124             this.el.removeClass(this.activeClass);
35125             return;
35126         }
35127         
35128         for(var i = 0; i < m.selectedBrick.length; i++) {
35129             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35130             b.el.removeClass(b.activeClass);
35131         }
35132         
35133         m.selectedBrick = [];
35134         
35135         m.selectedBrick.push(this.id);
35136         this.el.addClass(this.activeClass);
35137         return;
35138     },
35139     
35140     isSelected : function(){
35141         return this.el.hasClass(this.activeClass);
35142         
35143     }
35144 });
35145
35146 Roo.apply(Roo.bootstrap.MasonryBrick, {
35147     
35148     //groups: {},
35149     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35150      /**
35151     * register a Masonry Brick
35152     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35153     */
35154     
35155     register : function(brick)
35156     {
35157         //this.groups[brick.id] = brick;
35158         this.groups.add(brick.id, brick);
35159     },
35160     /**
35161     * fetch a  masonry brick based on the masonry brick ID
35162     * @param {string} the masonry brick to add
35163     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35164     */
35165     
35166     get: function(brick_id) 
35167     {
35168         // if (typeof(this.groups[brick_id]) == 'undefined') {
35169         //     return false;
35170         // }
35171         // return this.groups[brick_id] ;
35172         
35173         if(this.groups.key(brick_id)) {
35174             return this.groups.key(brick_id);
35175         }
35176         
35177         return false;
35178     }
35179     
35180     
35181     
35182 });
35183
35184  /*
35185  * - LGPL
35186  *
35187  * element
35188  * 
35189  */
35190
35191 /**
35192  * @class Roo.bootstrap.Brick
35193  * @extends Roo.bootstrap.Component
35194  * Bootstrap Brick class
35195  * 
35196  * @constructor
35197  * Create a new Brick
35198  * @param {Object} config The config object
35199  */
35200
35201 Roo.bootstrap.Brick = function(config){
35202     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35203     
35204     this.addEvents({
35205         // raw events
35206         /**
35207          * @event click
35208          * When a Brick is click
35209          * @param {Roo.bootstrap.Brick} this
35210          * @param {Roo.EventObject} e
35211          */
35212         "click" : true
35213     });
35214 };
35215
35216 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35217     
35218     /**
35219      * @cfg {String} title
35220      */   
35221     title : '',
35222     /**
35223      * @cfg {String} html
35224      */   
35225     html : '',
35226     /**
35227      * @cfg {String} bgimage
35228      */   
35229     bgimage : '',
35230     /**
35231      * @cfg {String} cls
35232      */   
35233     cls : '',
35234     /**
35235      * @cfg {String} href
35236      */   
35237     href : '',
35238     /**
35239      * @cfg {String} video
35240      */   
35241     video : '',
35242     /**
35243      * @cfg {Boolean} square
35244      */   
35245     square : true,
35246     
35247     getAutoCreate : function()
35248     {
35249         var cls = 'roo-brick';
35250         
35251         if(this.href.length){
35252             cls += ' roo-brick-link';
35253         }
35254         
35255         if(this.bgimage.length){
35256             cls += ' roo-brick-image';
35257         }
35258         
35259         if(!this.html.length && !this.bgimage.length){
35260             cls += ' roo-brick-center-title';
35261         }
35262         
35263         if(!this.html.length && this.bgimage.length){
35264             cls += ' roo-brick-bottom-title';
35265         }
35266         
35267         if(this.cls){
35268             cls += ' ' + this.cls;
35269         }
35270         
35271         var cfg = {
35272             tag: (this.href.length) ? 'a' : 'div',
35273             cls: cls,
35274             cn: [
35275                 {
35276                     tag: 'div',
35277                     cls: 'roo-brick-paragraph',
35278                     cn: []
35279                 }
35280             ]
35281         };
35282         
35283         if(this.href.length){
35284             cfg.href = this.href;
35285         }
35286         
35287         var cn = cfg.cn[0].cn;
35288         
35289         if(this.title.length){
35290             cn.push({
35291                 tag: 'h4',
35292                 cls: 'roo-brick-title',
35293                 html: this.title
35294             });
35295         }
35296         
35297         if(this.html.length){
35298             cn.push({
35299                 tag: 'p',
35300                 cls: 'roo-brick-text',
35301                 html: this.html
35302             });
35303         } else {
35304             cn.cls += ' hide';
35305         }
35306         
35307         if(this.bgimage.length){
35308             cfg.cn.push({
35309                 tag: 'img',
35310                 cls: 'roo-brick-image-view',
35311                 src: this.bgimage
35312             });
35313         }
35314         
35315         return cfg;
35316     },
35317     
35318     initEvents: function() 
35319     {
35320         if(this.title.length || this.html.length){
35321             this.el.on('mouseenter'  ,this.enter, this);
35322             this.el.on('mouseleave', this.leave, this);
35323         }
35324         
35325         Roo.EventManager.onWindowResize(this.resize, this); 
35326         
35327         if(this.bgimage.length){
35328             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35329             this.imageEl.on('load', this.onImageLoad, this);
35330             return;
35331         }
35332         
35333         this.resize();
35334     },
35335     
35336     onImageLoad : function()
35337     {
35338         this.resize();
35339     },
35340     
35341     resize : function()
35342     {
35343         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35344         
35345         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35346         
35347         if(this.bgimage.length){
35348             var image = this.el.select('.roo-brick-image-view', true).first();
35349             
35350             image.setWidth(paragraph.getWidth());
35351             
35352             if(this.square){
35353                 image.setHeight(paragraph.getWidth());
35354             }
35355             
35356             this.el.setHeight(image.getHeight());
35357             paragraph.setHeight(image.getHeight());
35358             
35359         }
35360         
35361     },
35362     
35363     enter: function(e, el)
35364     {
35365         e.preventDefault();
35366         
35367         if(this.bgimage.length){
35368             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35369             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35370         }
35371     },
35372     
35373     leave: function(e, el)
35374     {
35375         e.preventDefault();
35376         
35377         if(this.bgimage.length){
35378             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35379             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35380         }
35381     }
35382     
35383 });
35384
35385  
35386
35387  /*
35388  * - LGPL
35389  *
35390  * Number field 
35391  */
35392
35393 /**
35394  * @class Roo.bootstrap.NumberField
35395  * @extends Roo.bootstrap.Input
35396  * Bootstrap NumberField class
35397  * 
35398  * 
35399  * 
35400  * 
35401  * @constructor
35402  * Create a new NumberField
35403  * @param {Object} config The config object
35404  */
35405
35406 Roo.bootstrap.NumberField = function(config){
35407     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35408 };
35409
35410 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35411     
35412     /**
35413      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35414      */
35415     allowDecimals : true,
35416     /**
35417      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35418      */
35419     decimalSeparator : ".",
35420     /**
35421      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35422      */
35423     decimalPrecision : 2,
35424     /**
35425      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35426      */
35427     allowNegative : true,
35428     
35429     /**
35430      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35431      */
35432     allowZero: true,
35433     /**
35434      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35435      */
35436     minValue : Number.NEGATIVE_INFINITY,
35437     /**
35438      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35439      */
35440     maxValue : Number.MAX_VALUE,
35441     /**
35442      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35443      */
35444     minText : "The minimum value for this field is {0}",
35445     /**
35446      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35447      */
35448     maxText : "The maximum value for this field is {0}",
35449     /**
35450      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35451      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35452      */
35453     nanText : "{0} is not a valid number",
35454     /**
35455      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35456      */
35457     thousandsDelimiter : false,
35458     /**
35459      * @cfg {String} valueAlign alignment of value
35460      */
35461     valueAlign : "left",
35462
35463     getAutoCreate : function()
35464     {
35465         var hiddenInput = {
35466             tag: 'input',
35467             type: 'hidden',
35468             id: Roo.id(),
35469             cls: 'hidden-number-input'
35470         };
35471         
35472         if (this.name) {
35473             hiddenInput.name = this.name;
35474         }
35475         
35476         this.name = '';
35477         
35478         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35479         
35480         this.name = hiddenInput.name;
35481         
35482         if(cfg.cn.length > 0) {
35483             cfg.cn.push(hiddenInput);
35484         }
35485         
35486         return cfg;
35487     },
35488
35489     // private
35490     initEvents : function()
35491     {   
35492         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35493         
35494         var allowed = "0123456789";
35495         
35496         if(this.allowDecimals){
35497             allowed += this.decimalSeparator;
35498         }
35499         
35500         if(this.allowNegative){
35501             allowed += "-";
35502         }
35503         
35504         if(this.thousandsDelimiter) {
35505             allowed += ",";
35506         }
35507         
35508         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35509         
35510         var keyPress = function(e){
35511             
35512             var k = e.getKey();
35513             
35514             var c = e.getCharCode();
35515             
35516             if(
35517                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35518                     allowed.indexOf(String.fromCharCode(c)) === -1
35519             ){
35520                 e.stopEvent();
35521                 return;
35522             }
35523             
35524             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35525                 return;
35526             }
35527             
35528             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35529                 e.stopEvent();
35530             }
35531         };
35532         
35533         this.el.on("keypress", keyPress, this);
35534     },
35535     
35536     validateValue : function(value)
35537     {
35538         
35539         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35540             return false;
35541         }
35542         
35543         var num = this.parseValue(value);
35544         
35545         if(isNaN(num)){
35546             this.markInvalid(String.format(this.nanText, value));
35547             return false;
35548         }
35549         
35550         if(num < this.minValue){
35551             this.markInvalid(String.format(this.minText, this.minValue));
35552             return false;
35553         }
35554         
35555         if(num > this.maxValue){
35556             this.markInvalid(String.format(this.maxText, this.maxValue));
35557             return false;
35558         }
35559         
35560         return true;
35561     },
35562
35563     getValue : function()
35564     {
35565         var v = this.hiddenEl().getValue();
35566         
35567         return this.fixPrecision(this.parseValue(v));
35568     },
35569
35570     parseValue : function(value)
35571     {
35572         if(this.thousandsDelimiter) {
35573             value += "";
35574             r = new RegExp(",", "g");
35575             value = value.replace(r, "");
35576         }
35577         
35578         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35579         return isNaN(value) ? '' : value;
35580     },
35581
35582     fixPrecision : function(value)
35583     {
35584         if(this.thousandsDelimiter) {
35585             value += "";
35586             r = new RegExp(",", "g");
35587             value = value.replace(r, "");
35588         }
35589         
35590         var nan = isNaN(value);
35591         
35592         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35593             return nan ? '' : value;
35594         }
35595         return parseFloat(value).toFixed(this.decimalPrecision);
35596     },
35597
35598     setValue : function(v)
35599     {
35600         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35601         
35602         this.value = v;
35603         
35604         if(this.rendered){
35605             
35606             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35607             
35608             this.inputEl().dom.value = (v == '') ? '' :
35609                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35610             
35611             if(!this.allowZero && v === '0') {
35612                 this.hiddenEl().dom.value = '';
35613                 this.inputEl().dom.value = '';
35614             }
35615             
35616             this.validate();
35617         }
35618     },
35619
35620     decimalPrecisionFcn : function(v)
35621     {
35622         return Math.floor(v);
35623     },
35624
35625     beforeBlur : function()
35626     {
35627         var v = this.parseValue(this.getRawValue());
35628         
35629         if(v || v === 0 || v === ''){
35630             this.setValue(v);
35631         }
35632     },
35633     
35634     hiddenEl : function()
35635     {
35636         return this.el.select('input.hidden-number-input',true).first();
35637     }
35638     
35639 });
35640
35641  
35642
35643 /*
35644 * Licence: LGPL
35645 */
35646
35647 /**
35648  * @class Roo.bootstrap.DocumentSlider
35649  * @extends Roo.bootstrap.Component
35650  * Bootstrap DocumentSlider class
35651  * 
35652  * @constructor
35653  * Create a new DocumentViewer
35654  * @param {Object} config The config object
35655  */
35656
35657 Roo.bootstrap.DocumentSlider = function(config){
35658     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35659     
35660     this.files = [];
35661     
35662     this.addEvents({
35663         /**
35664          * @event initial
35665          * Fire after initEvent
35666          * @param {Roo.bootstrap.DocumentSlider} this
35667          */
35668         "initial" : true,
35669         /**
35670          * @event update
35671          * Fire after update
35672          * @param {Roo.bootstrap.DocumentSlider} this
35673          */
35674         "update" : true,
35675         /**
35676          * @event click
35677          * Fire after click
35678          * @param {Roo.bootstrap.DocumentSlider} this
35679          */
35680         "click" : true
35681     });
35682 };
35683
35684 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35685     
35686     files : false,
35687     
35688     indicator : 0,
35689     
35690     getAutoCreate : function()
35691     {
35692         var cfg = {
35693             tag : 'div',
35694             cls : 'roo-document-slider',
35695             cn : [
35696                 {
35697                     tag : 'div',
35698                     cls : 'roo-document-slider-header',
35699                     cn : [
35700                         {
35701                             tag : 'div',
35702                             cls : 'roo-document-slider-header-title'
35703                         }
35704                     ]
35705                 },
35706                 {
35707                     tag : 'div',
35708                     cls : 'roo-document-slider-body',
35709                     cn : [
35710                         {
35711                             tag : 'div',
35712                             cls : 'roo-document-slider-prev',
35713                             cn : [
35714                                 {
35715                                     tag : 'i',
35716                                     cls : 'fa fa-chevron-left'
35717                                 }
35718                             ]
35719                         },
35720                         {
35721                             tag : 'div',
35722                             cls : 'roo-document-slider-thumb',
35723                             cn : [
35724                                 {
35725                                     tag : 'img',
35726                                     cls : 'roo-document-slider-image'
35727                                 }
35728                             ]
35729                         },
35730                         {
35731                             tag : 'div',
35732                             cls : 'roo-document-slider-next',
35733                             cn : [
35734                                 {
35735                                     tag : 'i',
35736                                     cls : 'fa fa-chevron-right'
35737                                 }
35738                             ]
35739                         }
35740                     ]
35741                 }
35742             ]
35743         };
35744         
35745         return cfg;
35746     },
35747     
35748     initEvents : function()
35749     {
35750         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35751         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35752         
35753         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35754         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35755         
35756         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35757         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35758         
35759         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35760         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35761         
35762         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35763         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35764         
35765         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35766         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35767         
35768         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35769         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35770         
35771         this.thumbEl.on('click', this.onClick, this);
35772         
35773         this.prevIndicator.on('click', this.prev, this);
35774         
35775         this.nextIndicator.on('click', this.next, this);
35776         
35777     },
35778     
35779     initial : function()
35780     {
35781         if(this.files.length){
35782             this.indicator = 1;
35783             this.update()
35784         }
35785         
35786         this.fireEvent('initial', this);
35787     },
35788     
35789     update : function()
35790     {
35791         this.imageEl.attr('src', this.files[this.indicator - 1]);
35792         
35793         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35794         
35795         this.prevIndicator.show();
35796         
35797         if(this.indicator == 1){
35798             this.prevIndicator.hide();
35799         }
35800         
35801         this.nextIndicator.show();
35802         
35803         if(this.indicator == this.files.length){
35804             this.nextIndicator.hide();
35805         }
35806         
35807         this.thumbEl.scrollTo('top');
35808         
35809         this.fireEvent('update', this);
35810     },
35811     
35812     onClick : function(e)
35813     {
35814         e.preventDefault();
35815         
35816         this.fireEvent('click', this);
35817     },
35818     
35819     prev : function(e)
35820     {
35821         e.preventDefault();
35822         
35823         this.indicator = Math.max(1, this.indicator - 1);
35824         
35825         this.update();
35826     },
35827     
35828     next : function(e)
35829     {
35830         e.preventDefault();
35831         
35832         this.indicator = Math.min(this.files.length, this.indicator + 1);
35833         
35834         this.update();
35835     }
35836 });
35837 /*
35838  * - LGPL
35839  *
35840  * RadioSet
35841  *
35842  *
35843  */
35844
35845 /**
35846  * @class Roo.bootstrap.RadioSet
35847  * @extends Roo.bootstrap.Input
35848  * Bootstrap RadioSet class
35849  * @cfg {String} indicatorpos (left|right) default left
35850  * @cfg {Boolean} inline (true|false) inline the element (default true)
35851  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
35852  * @constructor
35853  * Create a new RadioSet
35854  * @param {Object} config The config object
35855  */
35856
35857 Roo.bootstrap.RadioSet = function(config){
35858     
35859     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
35860     
35861     this.radioes = [];
35862     
35863     Roo.bootstrap.RadioSet.register(this);
35864     
35865     this.addEvents({
35866         /**
35867         * @event check
35868         * Fires when the element is checked or unchecked.
35869         * @param {Roo.bootstrap.RadioSet} this This radio
35870         * @param {Roo.bootstrap.Radio} item The checked item
35871         */
35872        check : true,
35873        /**
35874         * @event click
35875         * Fires when the element is click.
35876         * @param {Roo.bootstrap.RadioSet} this This radio set
35877         * @param {Roo.bootstrap.Radio} item The checked item
35878         * @param {Roo.EventObject} e The event object
35879         */
35880        click : true
35881     });
35882     
35883 };
35884
35885 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
35886
35887     radioes : false,
35888     
35889     inline : true,
35890     
35891     weight : '',
35892     
35893     indicatorpos : 'left',
35894     
35895     getAutoCreate : function()
35896     {
35897         var label = {
35898             tag : 'label',
35899             cls : 'roo-radio-set-label',
35900             cn : [
35901                 {
35902                     tag : 'span',
35903                     html : this.fieldLabel
35904                 }
35905             ]
35906         };
35907         if (Roo.bootstrap.version == 3) {
35908             
35909             
35910             if(this.indicatorpos == 'left'){
35911                 label.cn.unshift({
35912                     tag : 'i',
35913                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
35914                     tooltip : 'This field is required'
35915                 });
35916             } else {
35917                 label.cn.push({
35918                     tag : 'i',
35919                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
35920                     tooltip : 'This field is required'
35921                 });
35922             }
35923         }
35924         var items = {
35925             tag : 'div',
35926             cls : 'roo-radio-set-items'
35927         };
35928         
35929         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
35930         
35931         if (align === 'left' && this.fieldLabel.length) {
35932             
35933             items = {
35934                 cls : "roo-radio-set-right", 
35935                 cn: [
35936                     items
35937                 ]
35938             };
35939             
35940             if(this.labelWidth > 12){
35941                 label.style = "width: " + this.labelWidth + 'px';
35942             }
35943             
35944             if(this.labelWidth < 13 && this.labelmd == 0){
35945                 this.labelmd = this.labelWidth;
35946             }
35947             
35948             if(this.labellg > 0){
35949                 label.cls += ' col-lg-' + this.labellg;
35950                 items.cls += ' col-lg-' + (12 - this.labellg);
35951             }
35952             
35953             if(this.labelmd > 0){
35954                 label.cls += ' col-md-' + this.labelmd;
35955                 items.cls += ' col-md-' + (12 - this.labelmd);
35956             }
35957             
35958             if(this.labelsm > 0){
35959                 label.cls += ' col-sm-' + this.labelsm;
35960                 items.cls += ' col-sm-' + (12 - this.labelsm);
35961             }
35962             
35963             if(this.labelxs > 0){
35964                 label.cls += ' col-xs-' + this.labelxs;
35965                 items.cls += ' col-xs-' + (12 - this.labelxs);
35966             }
35967         }
35968         
35969         var cfg = {
35970             tag : 'div',
35971             cls : 'roo-radio-set',
35972             cn : [
35973                 {
35974                     tag : 'input',
35975                     cls : 'roo-radio-set-input',
35976                     type : 'hidden',
35977                     name : this.name,
35978                     value : this.value ? this.value :  ''
35979                 },
35980                 label,
35981                 items
35982             ]
35983         };
35984         
35985         if(this.weight.length){
35986             cfg.cls += ' roo-radio-' + this.weight;
35987         }
35988         
35989         if(this.inline) {
35990             cfg.cls += ' roo-radio-set-inline';
35991         }
35992         
35993         var settings=this;
35994         ['xs','sm','md','lg'].map(function(size){
35995             if (settings[size]) {
35996                 cfg.cls += ' col-' + size + '-' + settings[size];
35997             }
35998         });
35999         
36000         return cfg;
36001         
36002     },
36003
36004     initEvents : function()
36005     {
36006         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36007         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36008         
36009         if(!this.fieldLabel.length){
36010             this.labelEl.hide();
36011         }
36012         
36013         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36014         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36015         
36016         this.indicator = this.indicatorEl();
36017         
36018         if(this.indicator){
36019             this.indicator.addClass('invisible');
36020         }
36021         
36022         this.originalValue = this.getValue();
36023         
36024     },
36025     
36026     inputEl: function ()
36027     {
36028         return this.el.select('.roo-radio-set-input', true).first();
36029     },
36030     
36031     getChildContainer : function()
36032     {
36033         return this.itemsEl;
36034     },
36035     
36036     register : function(item)
36037     {
36038         this.radioes.push(item);
36039         
36040     },
36041     
36042     validate : function()
36043     {   
36044         if(this.getVisibilityEl().hasClass('hidden')){
36045             return true;
36046         }
36047         
36048         var valid = false;
36049         
36050         Roo.each(this.radioes, function(i){
36051             if(!i.checked){
36052                 return;
36053             }
36054             
36055             valid = true;
36056             return false;
36057         });
36058         
36059         if(this.allowBlank) {
36060             return true;
36061         }
36062         
36063         if(this.disabled || valid){
36064             this.markValid();
36065             return true;
36066         }
36067         
36068         this.markInvalid();
36069         return false;
36070         
36071     },
36072     
36073     markValid : function()
36074     {
36075         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36076             this.indicatorEl().removeClass('visible');
36077             this.indicatorEl().addClass('invisible');
36078         }
36079         
36080         
36081         if (Roo.bootstrap.version == 3) {
36082             this.el.removeClass([this.invalidClass, this.validClass]);
36083             this.el.addClass(this.validClass);
36084         } else {
36085             this.el.removeClass(['is-invalid','is-valid']);
36086             this.el.addClass(['is-valid']);
36087         }
36088         this.fireEvent('valid', this);
36089     },
36090     
36091     markInvalid : function(msg)
36092     {
36093         if(this.allowBlank || this.disabled){
36094             return;
36095         }
36096         
36097         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36098             this.indicatorEl().removeClass('invisible');
36099             this.indicatorEl().addClass('visible');
36100         }
36101         if (Roo.bootstrap.version == 3) {
36102             this.el.removeClass([this.invalidClass, this.validClass]);
36103             this.el.addClass(this.invalidClass);
36104         } else {
36105             this.el.removeClass(['is-invalid','is-valid']);
36106             this.el.addClass(['is-invalid']);
36107         }
36108         
36109         this.fireEvent('invalid', this, msg);
36110         
36111     },
36112     
36113     setValue : function(v, suppressEvent)
36114     {   
36115         if(this.value === v){
36116             return;
36117         }
36118         
36119         this.value = v;
36120         
36121         if(this.rendered){
36122             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36123         }
36124         
36125         Roo.each(this.radioes, function(i){
36126             i.checked = false;
36127             i.el.removeClass('checked');
36128         });
36129         
36130         Roo.each(this.radioes, function(i){
36131             
36132             if(i.value === v || i.value.toString() === v.toString()){
36133                 i.checked = true;
36134                 i.el.addClass('checked');
36135                 
36136                 if(suppressEvent !== true){
36137                     this.fireEvent('check', this, i);
36138                 }
36139                 
36140                 return false;
36141             }
36142             
36143         }, this);
36144         
36145         this.validate();
36146     },
36147     
36148     clearInvalid : function(){
36149         
36150         if(!this.el || this.preventMark){
36151             return;
36152         }
36153         
36154         this.el.removeClass([this.invalidClass]);
36155         
36156         this.fireEvent('valid', this);
36157     }
36158     
36159 });
36160
36161 Roo.apply(Roo.bootstrap.RadioSet, {
36162     
36163     groups: {},
36164     
36165     register : function(set)
36166     {
36167         this.groups[set.name] = set;
36168     },
36169     
36170     get: function(name) 
36171     {
36172         if (typeof(this.groups[name]) == 'undefined') {
36173             return false;
36174         }
36175         
36176         return this.groups[name] ;
36177     }
36178     
36179 });
36180 /*
36181  * Based on:
36182  * Ext JS Library 1.1.1
36183  * Copyright(c) 2006-2007, Ext JS, LLC.
36184  *
36185  * Originally Released Under LGPL - original licence link has changed is not relivant.
36186  *
36187  * Fork - LGPL
36188  * <script type="text/javascript">
36189  */
36190
36191
36192 /**
36193  * @class Roo.bootstrap.SplitBar
36194  * @extends Roo.util.Observable
36195  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36196  * <br><br>
36197  * Usage:
36198  * <pre><code>
36199 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36200                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36201 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36202 split.minSize = 100;
36203 split.maxSize = 600;
36204 split.animate = true;
36205 split.on('moved', splitterMoved);
36206 </code></pre>
36207  * @constructor
36208  * Create a new SplitBar
36209  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36210  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36211  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36212  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36213                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36214                         position of the SplitBar).
36215  */
36216 Roo.bootstrap.SplitBar = function(cfg){
36217     
36218     /** @private */
36219     
36220     //{
36221     //  dragElement : elm
36222     //  resizingElement: el,
36223         // optional..
36224     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36225     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36226         // existingProxy ???
36227     //}
36228     
36229     this.el = Roo.get(cfg.dragElement, true);
36230     this.el.dom.unselectable = "on";
36231     /** @private */
36232     this.resizingEl = Roo.get(cfg.resizingElement, true);
36233
36234     /**
36235      * @private
36236      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36237      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36238      * @type Number
36239      */
36240     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36241     
36242     /**
36243      * The minimum size of the resizing element. (Defaults to 0)
36244      * @type Number
36245      */
36246     this.minSize = 0;
36247     
36248     /**
36249      * The maximum size of the resizing element. (Defaults to 2000)
36250      * @type Number
36251      */
36252     this.maxSize = 2000;
36253     
36254     /**
36255      * Whether to animate the transition to the new size
36256      * @type Boolean
36257      */
36258     this.animate = false;
36259     
36260     /**
36261      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36262      * @type Boolean
36263      */
36264     this.useShim = false;
36265     
36266     /** @private */
36267     this.shim = null;
36268     
36269     if(!cfg.existingProxy){
36270         /** @private */
36271         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36272     }else{
36273         this.proxy = Roo.get(cfg.existingProxy).dom;
36274     }
36275     /** @private */
36276     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36277     
36278     /** @private */
36279     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36280     
36281     /** @private */
36282     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36283     
36284     /** @private */
36285     this.dragSpecs = {};
36286     
36287     /**
36288      * @private The adapter to use to positon and resize elements
36289      */
36290     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36291     this.adapter.init(this);
36292     
36293     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36294         /** @private */
36295         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36296         this.el.addClass("roo-splitbar-h");
36297     }else{
36298         /** @private */
36299         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36300         this.el.addClass("roo-splitbar-v");
36301     }
36302     
36303     this.addEvents({
36304         /**
36305          * @event resize
36306          * Fires when the splitter is moved (alias for {@link #event-moved})
36307          * @param {Roo.bootstrap.SplitBar} this
36308          * @param {Number} newSize the new width or height
36309          */
36310         "resize" : true,
36311         /**
36312          * @event moved
36313          * Fires when the splitter is moved
36314          * @param {Roo.bootstrap.SplitBar} this
36315          * @param {Number} newSize the new width or height
36316          */
36317         "moved" : true,
36318         /**
36319          * @event beforeresize
36320          * Fires before the splitter is dragged
36321          * @param {Roo.bootstrap.SplitBar} this
36322          */
36323         "beforeresize" : true,
36324
36325         "beforeapply" : true
36326     });
36327
36328     Roo.util.Observable.call(this);
36329 };
36330
36331 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36332     onStartProxyDrag : function(x, y){
36333         this.fireEvent("beforeresize", this);
36334         if(!this.overlay){
36335             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36336             o.unselectable();
36337             o.enableDisplayMode("block");
36338             // all splitbars share the same overlay
36339             Roo.bootstrap.SplitBar.prototype.overlay = o;
36340         }
36341         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36342         this.overlay.show();
36343         Roo.get(this.proxy).setDisplayed("block");
36344         var size = this.adapter.getElementSize(this);
36345         this.activeMinSize = this.getMinimumSize();;
36346         this.activeMaxSize = this.getMaximumSize();;
36347         var c1 = size - this.activeMinSize;
36348         var c2 = Math.max(this.activeMaxSize - size, 0);
36349         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36350             this.dd.resetConstraints();
36351             this.dd.setXConstraint(
36352                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36353                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36354             );
36355             this.dd.setYConstraint(0, 0);
36356         }else{
36357             this.dd.resetConstraints();
36358             this.dd.setXConstraint(0, 0);
36359             this.dd.setYConstraint(
36360                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36361                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36362             );
36363          }
36364         this.dragSpecs.startSize = size;
36365         this.dragSpecs.startPoint = [x, y];
36366         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36367     },
36368     
36369     /** 
36370      * @private Called after the drag operation by the DDProxy
36371      */
36372     onEndProxyDrag : function(e){
36373         Roo.get(this.proxy).setDisplayed(false);
36374         var endPoint = Roo.lib.Event.getXY(e);
36375         if(this.overlay){
36376             this.overlay.hide();
36377         }
36378         var newSize;
36379         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36380             newSize = this.dragSpecs.startSize + 
36381                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36382                     endPoint[0] - this.dragSpecs.startPoint[0] :
36383                     this.dragSpecs.startPoint[0] - endPoint[0]
36384                 );
36385         }else{
36386             newSize = this.dragSpecs.startSize + 
36387                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36388                     endPoint[1] - this.dragSpecs.startPoint[1] :
36389                     this.dragSpecs.startPoint[1] - endPoint[1]
36390                 );
36391         }
36392         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36393         if(newSize != this.dragSpecs.startSize){
36394             if(this.fireEvent('beforeapply', this, newSize) !== false){
36395                 this.adapter.setElementSize(this, newSize);
36396                 this.fireEvent("moved", this, newSize);
36397                 this.fireEvent("resize", this, newSize);
36398             }
36399         }
36400     },
36401     
36402     /**
36403      * Get the adapter this SplitBar uses
36404      * @return The adapter object
36405      */
36406     getAdapter : function(){
36407         return this.adapter;
36408     },
36409     
36410     /**
36411      * Set the adapter this SplitBar uses
36412      * @param {Object} adapter A SplitBar adapter object
36413      */
36414     setAdapter : function(adapter){
36415         this.adapter = adapter;
36416         this.adapter.init(this);
36417     },
36418     
36419     /**
36420      * Gets the minimum size for the resizing element
36421      * @return {Number} The minimum size
36422      */
36423     getMinimumSize : function(){
36424         return this.minSize;
36425     },
36426     
36427     /**
36428      * Sets the minimum size for the resizing element
36429      * @param {Number} minSize The minimum size
36430      */
36431     setMinimumSize : function(minSize){
36432         this.minSize = minSize;
36433     },
36434     
36435     /**
36436      * Gets the maximum size for the resizing element
36437      * @return {Number} The maximum size
36438      */
36439     getMaximumSize : function(){
36440         return this.maxSize;
36441     },
36442     
36443     /**
36444      * Sets the maximum size for the resizing element
36445      * @param {Number} maxSize The maximum size
36446      */
36447     setMaximumSize : function(maxSize){
36448         this.maxSize = maxSize;
36449     },
36450     
36451     /**
36452      * Sets the initialize size for the resizing element
36453      * @param {Number} size The initial size
36454      */
36455     setCurrentSize : function(size){
36456         var oldAnimate = this.animate;
36457         this.animate = false;
36458         this.adapter.setElementSize(this, size);
36459         this.animate = oldAnimate;
36460     },
36461     
36462     /**
36463      * Destroy this splitbar. 
36464      * @param {Boolean} removeEl True to remove the element
36465      */
36466     destroy : function(removeEl){
36467         if(this.shim){
36468             this.shim.remove();
36469         }
36470         this.dd.unreg();
36471         this.proxy.parentNode.removeChild(this.proxy);
36472         if(removeEl){
36473             this.el.remove();
36474         }
36475     }
36476 });
36477
36478 /**
36479  * @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.
36480  */
36481 Roo.bootstrap.SplitBar.createProxy = function(dir){
36482     var proxy = new Roo.Element(document.createElement("div"));
36483     proxy.unselectable();
36484     var cls = 'roo-splitbar-proxy';
36485     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36486     document.body.appendChild(proxy.dom);
36487     return proxy.dom;
36488 };
36489
36490 /** 
36491  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36492  * Default Adapter. It assumes the splitter and resizing element are not positioned
36493  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36494  */
36495 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36496 };
36497
36498 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36499     // do nothing for now
36500     init : function(s){
36501     
36502     },
36503     /**
36504      * Called before drag operations to get the current size of the resizing element. 
36505      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36506      */
36507      getElementSize : function(s){
36508         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36509             return s.resizingEl.getWidth();
36510         }else{
36511             return s.resizingEl.getHeight();
36512         }
36513     },
36514     
36515     /**
36516      * Called after drag operations to set the size of the resizing element.
36517      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36518      * @param {Number} newSize The new size to set
36519      * @param {Function} onComplete A function to be invoked when resizing is complete
36520      */
36521     setElementSize : function(s, newSize, onComplete){
36522         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36523             if(!s.animate){
36524                 s.resizingEl.setWidth(newSize);
36525                 if(onComplete){
36526                     onComplete(s, newSize);
36527                 }
36528             }else{
36529                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36530             }
36531         }else{
36532             
36533             if(!s.animate){
36534                 s.resizingEl.setHeight(newSize);
36535                 if(onComplete){
36536                     onComplete(s, newSize);
36537                 }
36538             }else{
36539                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36540             }
36541         }
36542     }
36543 };
36544
36545 /** 
36546  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36547  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36548  * Adapter that  moves the splitter element to align with the resized sizing element. 
36549  * Used with an absolute positioned SplitBar.
36550  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36551  * document.body, make sure you assign an id to the body element.
36552  */
36553 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36554     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36555     this.container = Roo.get(container);
36556 };
36557
36558 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36559     init : function(s){
36560         this.basic.init(s);
36561     },
36562     
36563     getElementSize : function(s){
36564         return this.basic.getElementSize(s);
36565     },
36566     
36567     setElementSize : function(s, newSize, onComplete){
36568         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36569     },
36570     
36571     moveSplitter : function(s){
36572         var yes = Roo.bootstrap.SplitBar;
36573         switch(s.placement){
36574             case yes.LEFT:
36575                 s.el.setX(s.resizingEl.getRight());
36576                 break;
36577             case yes.RIGHT:
36578                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36579                 break;
36580             case yes.TOP:
36581                 s.el.setY(s.resizingEl.getBottom());
36582                 break;
36583             case yes.BOTTOM:
36584                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36585                 break;
36586         }
36587     }
36588 };
36589
36590 /**
36591  * Orientation constant - Create a vertical SplitBar
36592  * @static
36593  * @type Number
36594  */
36595 Roo.bootstrap.SplitBar.VERTICAL = 1;
36596
36597 /**
36598  * Orientation constant - Create a horizontal SplitBar
36599  * @static
36600  * @type Number
36601  */
36602 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36603
36604 /**
36605  * Placement constant - The resizing element is to the left of the splitter element
36606  * @static
36607  * @type Number
36608  */
36609 Roo.bootstrap.SplitBar.LEFT = 1;
36610
36611 /**
36612  * Placement constant - The resizing element is to the right of the splitter element
36613  * @static
36614  * @type Number
36615  */
36616 Roo.bootstrap.SplitBar.RIGHT = 2;
36617
36618 /**
36619  * Placement constant - The resizing element is positioned above the splitter element
36620  * @static
36621  * @type Number
36622  */
36623 Roo.bootstrap.SplitBar.TOP = 3;
36624
36625 /**
36626  * Placement constant - The resizing element is positioned under splitter element
36627  * @static
36628  * @type Number
36629  */
36630 Roo.bootstrap.SplitBar.BOTTOM = 4;
36631 Roo.namespace("Roo.bootstrap.layout");/*
36632  * Based on:
36633  * Ext JS Library 1.1.1
36634  * Copyright(c) 2006-2007, Ext JS, LLC.
36635  *
36636  * Originally Released Under LGPL - original licence link has changed is not relivant.
36637  *
36638  * Fork - LGPL
36639  * <script type="text/javascript">
36640  */
36641
36642 /**
36643  * @class Roo.bootstrap.layout.Manager
36644  * @extends Roo.bootstrap.Component
36645  * Base class for layout managers.
36646  */
36647 Roo.bootstrap.layout.Manager = function(config)
36648 {
36649     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36650
36651
36652
36653
36654
36655     /** false to disable window resize monitoring @type Boolean */
36656     this.monitorWindowResize = true;
36657     this.regions = {};
36658     this.addEvents({
36659         /**
36660          * @event layout
36661          * Fires when a layout is performed.
36662          * @param {Roo.LayoutManager} this
36663          */
36664         "layout" : true,
36665         /**
36666          * @event regionresized
36667          * Fires when the user resizes a region.
36668          * @param {Roo.LayoutRegion} region The resized region
36669          * @param {Number} newSize The new size (width for east/west, height for north/south)
36670          */
36671         "regionresized" : true,
36672         /**
36673          * @event regioncollapsed
36674          * Fires when a region is collapsed.
36675          * @param {Roo.LayoutRegion} region The collapsed region
36676          */
36677         "regioncollapsed" : true,
36678         /**
36679          * @event regionexpanded
36680          * Fires when a region is expanded.
36681          * @param {Roo.LayoutRegion} region The expanded region
36682          */
36683         "regionexpanded" : true
36684     });
36685     this.updating = false;
36686
36687     if (config.el) {
36688         this.el = Roo.get(config.el);
36689         this.initEvents();
36690     }
36691
36692 };
36693
36694 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36695
36696
36697     regions : null,
36698
36699     monitorWindowResize : true,
36700
36701
36702     updating : false,
36703
36704
36705     onRender : function(ct, position)
36706     {
36707         if(!this.el){
36708             this.el = Roo.get(ct);
36709             this.initEvents();
36710         }
36711         //this.fireEvent('render',this);
36712     },
36713
36714
36715     initEvents: function()
36716     {
36717
36718
36719         // ie scrollbar fix
36720         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36721             document.body.scroll = "no";
36722         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36723             this.el.position('relative');
36724         }
36725         this.id = this.el.id;
36726         this.el.addClass("roo-layout-container");
36727         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36728         if(this.el.dom != document.body ) {
36729             this.el.on('resize', this.layout,this);
36730             this.el.on('show', this.layout,this);
36731         }
36732
36733     },
36734
36735     /**
36736      * Returns true if this layout is currently being updated
36737      * @return {Boolean}
36738      */
36739     isUpdating : function(){
36740         return this.updating;
36741     },
36742
36743     /**
36744      * Suspend the LayoutManager from doing auto-layouts while
36745      * making multiple add or remove calls
36746      */
36747     beginUpdate : function(){
36748         this.updating = true;
36749     },
36750
36751     /**
36752      * Restore auto-layouts and optionally disable the manager from performing a layout
36753      * @param {Boolean} noLayout true to disable a layout update
36754      */
36755     endUpdate : function(noLayout){
36756         this.updating = false;
36757         if(!noLayout){
36758             this.layout();
36759         }
36760     },
36761
36762     layout: function(){
36763         // abstract...
36764     },
36765
36766     onRegionResized : function(region, newSize){
36767         this.fireEvent("regionresized", region, newSize);
36768         this.layout();
36769     },
36770
36771     onRegionCollapsed : function(region){
36772         this.fireEvent("regioncollapsed", region);
36773     },
36774
36775     onRegionExpanded : function(region){
36776         this.fireEvent("regionexpanded", region);
36777     },
36778
36779     /**
36780      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36781      * performs box-model adjustments.
36782      * @return {Object} The size as an object {width: (the width), height: (the height)}
36783      */
36784     getViewSize : function()
36785     {
36786         var size;
36787         if(this.el.dom != document.body){
36788             size = this.el.getSize();
36789         }else{
36790             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36791         }
36792         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36793         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36794         return size;
36795     },
36796
36797     /**
36798      * Returns the Element this layout is bound to.
36799      * @return {Roo.Element}
36800      */
36801     getEl : function(){
36802         return this.el;
36803     },
36804
36805     /**
36806      * Returns the specified region.
36807      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36808      * @return {Roo.LayoutRegion}
36809      */
36810     getRegion : function(target){
36811         return this.regions[target.toLowerCase()];
36812     },
36813
36814     onWindowResize : function(){
36815         if(this.monitorWindowResize){
36816             this.layout();
36817         }
36818     }
36819 });
36820 /*
36821  * Based on:
36822  * Ext JS Library 1.1.1
36823  * Copyright(c) 2006-2007, Ext JS, LLC.
36824  *
36825  * Originally Released Under LGPL - original licence link has changed is not relivant.
36826  *
36827  * Fork - LGPL
36828  * <script type="text/javascript">
36829  */
36830 /**
36831  * @class Roo.bootstrap.layout.Border
36832  * @extends Roo.bootstrap.layout.Manager
36833  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36834  * please see: examples/bootstrap/nested.html<br><br>
36835  
36836 <b>The container the layout is rendered into can be either the body element or any other element.
36837 If it is not the body element, the container needs to either be an absolute positioned element,
36838 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36839 the container size if it is not the body element.</b>
36840
36841 * @constructor
36842 * Create a new Border
36843 * @param {Object} config Configuration options
36844  */
36845 Roo.bootstrap.layout.Border = function(config){
36846     config = config || {};
36847     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
36848     
36849     
36850     
36851     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36852         if(config[region]){
36853             config[region].region = region;
36854             this.addRegion(config[region]);
36855         }
36856     },this);
36857     
36858 };
36859
36860 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
36861
36862 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
36863     
36864     parent : false, // this might point to a 'nest' or a ???
36865     
36866     /**
36867      * Creates and adds a new region if it doesn't already exist.
36868      * @param {String} target The target region key (north, south, east, west or center).
36869      * @param {Object} config The regions config object
36870      * @return {BorderLayoutRegion} The new region
36871      */
36872     addRegion : function(config)
36873     {
36874         if(!this.regions[config.region]){
36875             var r = this.factory(config);
36876             this.bindRegion(r);
36877         }
36878         return this.regions[config.region];
36879     },
36880
36881     // private (kinda)
36882     bindRegion : function(r){
36883         this.regions[r.config.region] = r;
36884         
36885         r.on("visibilitychange",    this.layout, this);
36886         r.on("paneladded",          this.layout, this);
36887         r.on("panelremoved",        this.layout, this);
36888         r.on("invalidated",         this.layout, this);
36889         r.on("resized",             this.onRegionResized, this);
36890         r.on("collapsed",           this.onRegionCollapsed, this);
36891         r.on("expanded",            this.onRegionExpanded, this);
36892     },
36893
36894     /**
36895      * Performs a layout update.
36896      */
36897     layout : function()
36898     {
36899         if(this.updating) {
36900             return;
36901         }
36902         
36903         // render all the rebions if they have not been done alreayd?
36904         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
36905             if(this.regions[region] && !this.regions[region].bodyEl){
36906                 this.regions[region].onRender(this.el)
36907             }
36908         },this);
36909         
36910         var size = this.getViewSize();
36911         var w = size.width;
36912         var h = size.height;
36913         var centerW = w;
36914         var centerH = h;
36915         var centerY = 0;
36916         var centerX = 0;
36917         //var x = 0, y = 0;
36918
36919         var rs = this.regions;
36920         var north = rs["north"];
36921         var south = rs["south"]; 
36922         var west = rs["west"];
36923         var east = rs["east"];
36924         var center = rs["center"];
36925         //if(this.hideOnLayout){ // not supported anymore
36926             //c.el.setStyle("display", "none");
36927         //}
36928         if(north && north.isVisible()){
36929             var b = north.getBox();
36930             var m = north.getMargins();
36931             b.width = w - (m.left+m.right);
36932             b.x = m.left;
36933             b.y = m.top;
36934             centerY = b.height + b.y + m.bottom;
36935             centerH -= centerY;
36936             north.updateBox(this.safeBox(b));
36937         }
36938         if(south && south.isVisible()){
36939             var b = south.getBox();
36940             var m = south.getMargins();
36941             b.width = w - (m.left+m.right);
36942             b.x = m.left;
36943             var totalHeight = (b.height + m.top + m.bottom);
36944             b.y = h - totalHeight + m.top;
36945             centerH -= totalHeight;
36946             south.updateBox(this.safeBox(b));
36947         }
36948         if(west && west.isVisible()){
36949             var b = west.getBox();
36950             var m = west.getMargins();
36951             b.height = centerH - (m.top+m.bottom);
36952             b.x = m.left;
36953             b.y = centerY + m.top;
36954             var totalWidth = (b.width + m.left + m.right);
36955             centerX += totalWidth;
36956             centerW -= totalWidth;
36957             west.updateBox(this.safeBox(b));
36958         }
36959         if(east && east.isVisible()){
36960             var b = east.getBox();
36961             var m = east.getMargins();
36962             b.height = centerH - (m.top+m.bottom);
36963             var totalWidth = (b.width + m.left + m.right);
36964             b.x = w - totalWidth + m.left;
36965             b.y = centerY + m.top;
36966             centerW -= totalWidth;
36967             east.updateBox(this.safeBox(b));
36968         }
36969         if(center){
36970             var m = center.getMargins();
36971             var centerBox = {
36972                 x: centerX + m.left,
36973                 y: centerY + m.top,
36974                 width: centerW - (m.left+m.right),
36975                 height: centerH - (m.top+m.bottom)
36976             };
36977             //if(this.hideOnLayout){
36978                 //center.el.setStyle("display", "block");
36979             //}
36980             center.updateBox(this.safeBox(centerBox));
36981         }
36982         this.el.repaint();
36983         this.fireEvent("layout", this);
36984     },
36985
36986     // private
36987     safeBox : function(box){
36988         box.width = Math.max(0, box.width);
36989         box.height = Math.max(0, box.height);
36990         return box;
36991     },
36992
36993     /**
36994      * Adds a ContentPanel (or subclass) to this layout.
36995      * @param {String} target The target region key (north, south, east, west or center).
36996      * @param {Roo.ContentPanel} panel The panel to add
36997      * @return {Roo.ContentPanel} The added panel
36998      */
36999     add : function(target, panel){
37000          
37001         target = target.toLowerCase();
37002         return this.regions[target].add(panel);
37003     },
37004
37005     /**
37006      * Remove a ContentPanel (or subclass) to this layout.
37007      * @param {String} target The target region key (north, south, east, west or center).
37008      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37009      * @return {Roo.ContentPanel} The removed panel
37010      */
37011     remove : function(target, panel){
37012         target = target.toLowerCase();
37013         return this.regions[target].remove(panel);
37014     },
37015
37016     /**
37017      * Searches all regions for a panel with the specified id
37018      * @param {String} panelId
37019      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37020      */
37021     findPanel : function(panelId){
37022         var rs = this.regions;
37023         for(var target in rs){
37024             if(typeof rs[target] != "function"){
37025                 var p = rs[target].getPanel(panelId);
37026                 if(p){
37027                     return p;
37028                 }
37029             }
37030         }
37031         return null;
37032     },
37033
37034     /**
37035      * Searches all regions for a panel with the specified id and activates (shows) it.
37036      * @param {String/ContentPanel} panelId The panels id or the panel itself
37037      * @return {Roo.ContentPanel} The shown panel or null
37038      */
37039     showPanel : function(panelId) {
37040       var rs = this.regions;
37041       for(var target in rs){
37042          var r = rs[target];
37043          if(typeof r != "function"){
37044             if(r.hasPanel(panelId)){
37045                return r.showPanel(panelId);
37046             }
37047          }
37048       }
37049       return null;
37050    },
37051
37052    /**
37053      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37054      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37055      */
37056    /*
37057     restoreState : function(provider){
37058         if(!provider){
37059             provider = Roo.state.Manager;
37060         }
37061         var sm = new Roo.LayoutStateManager();
37062         sm.init(this, provider);
37063     },
37064 */
37065  
37066  
37067     /**
37068      * Adds a xtype elements to the layout.
37069      * <pre><code>
37070
37071 layout.addxtype({
37072        xtype : 'ContentPanel',
37073        region: 'west',
37074        items: [ .... ]
37075    }
37076 );
37077
37078 layout.addxtype({
37079         xtype : 'NestedLayoutPanel',
37080         region: 'west',
37081         layout: {
37082            center: { },
37083            west: { }   
37084         },
37085         items : [ ... list of content panels or nested layout panels.. ]
37086    }
37087 );
37088 </code></pre>
37089      * @param {Object} cfg Xtype definition of item to add.
37090      */
37091     addxtype : function(cfg)
37092     {
37093         // basically accepts a pannel...
37094         // can accept a layout region..!?!?
37095         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37096         
37097         
37098         // theory?  children can only be panels??
37099         
37100         //if (!cfg.xtype.match(/Panel$/)) {
37101         //    return false;
37102         //}
37103         var ret = false;
37104         
37105         if (typeof(cfg.region) == 'undefined') {
37106             Roo.log("Failed to add Panel, region was not set");
37107             Roo.log(cfg);
37108             return false;
37109         }
37110         var region = cfg.region;
37111         delete cfg.region;
37112         
37113           
37114         var xitems = [];
37115         if (cfg.items) {
37116             xitems = cfg.items;
37117             delete cfg.items;
37118         }
37119         var nb = false;
37120         
37121         if ( region == 'center') {
37122             Roo.log("Center: " + cfg.title);
37123         }
37124         
37125         
37126         switch(cfg.xtype) 
37127         {
37128             case 'Content':  // ContentPanel (el, cfg)
37129             case 'Scroll':  // ContentPanel (el, cfg)
37130             case 'View': 
37131                 cfg.autoCreate = cfg.autoCreate || true;
37132                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37133                 //} else {
37134                 //    var el = this.el.createChild();
37135                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37136                 //}
37137                 
37138                 this.add(region, ret);
37139                 break;
37140             
37141             /*
37142             case 'TreePanel': // our new panel!
37143                 cfg.el = this.el.createChild();
37144                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37145                 this.add(region, ret);
37146                 break;
37147             */
37148             
37149             case 'Nest': 
37150                 // create a new Layout (which is  a Border Layout...
37151                 
37152                 var clayout = cfg.layout;
37153                 clayout.el  = this.el.createChild();
37154                 clayout.items   = clayout.items  || [];
37155                 
37156                 delete cfg.layout;
37157                 
37158                 // replace this exitems with the clayout ones..
37159                 xitems = clayout.items;
37160                  
37161                 // force background off if it's in center...
37162                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37163                     cfg.background = false;
37164                 }
37165                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37166                 
37167                 
37168                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37169                 //console.log('adding nested layout panel '  + cfg.toSource());
37170                 this.add(region, ret);
37171                 nb = {}; /// find first...
37172                 break;
37173             
37174             case 'Grid':
37175                 
37176                 // needs grid and region
37177                 
37178                 //var el = this.getRegion(region).el.createChild();
37179                 /*
37180                  *var el = this.el.createChild();
37181                 // create the grid first...
37182                 cfg.grid.container = el;
37183                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37184                 */
37185                 
37186                 if (region == 'center' && this.active ) {
37187                     cfg.background = false;
37188                 }
37189                 
37190                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37191                 
37192                 this.add(region, ret);
37193                 /*
37194                 if (cfg.background) {
37195                     // render grid on panel activation (if panel background)
37196                     ret.on('activate', function(gp) {
37197                         if (!gp.grid.rendered) {
37198                     //        gp.grid.render(el);
37199                         }
37200                     });
37201                 } else {
37202                   //  cfg.grid.render(el);
37203                 }
37204                 */
37205                 break;
37206            
37207            
37208             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37209                 // it was the old xcomponent building that caused this before.
37210                 // espeically if border is the top element in the tree.
37211                 ret = this;
37212                 break; 
37213                 
37214                     
37215                 
37216                 
37217                 
37218             default:
37219                 /*
37220                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37221                     
37222                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37223                     this.add(region, ret);
37224                 } else {
37225                 */
37226                     Roo.log(cfg);
37227                     throw "Can not add '" + cfg.xtype + "' to Border";
37228                     return null;
37229              
37230                                 
37231              
37232         }
37233         this.beginUpdate();
37234         // add children..
37235         var region = '';
37236         var abn = {};
37237         Roo.each(xitems, function(i)  {
37238             region = nb && i.region ? i.region : false;
37239             
37240             var add = ret.addxtype(i);
37241            
37242             if (region) {
37243                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37244                 if (!i.background) {
37245                     abn[region] = nb[region] ;
37246                 }
37247             }
37248             
37249         });
37250         this.endUpdate();
37251
37252         // make the last non-background panel active..
37253         //if (nb) { Roo.log(abn); }
37254         if (nb) {
37255             
37256             for(var r in abn) {
37257                 region = this.getRegion(r);
37258                 if (region) {
37259                     // tried using nb[r], but it does not work..
37260                      
37261                     region.showPanel(abn[r]);
37262                    
37263                 }
37264             }
37265         }
37266         return ret;
37267         
37268     },
37269     
37270     
37271 // private
37272     factory : function(cfg)
37273     {
37274         
37275         var validRegions = Roo.bootstrap.layout.Border.regions;
37276
37277         var target = cfg.region;
37278         cfg.mgr = this;
37279         
37280         var r = Roo.bootstrap.layout;
37281         Roo.log(target);
37282         switch(target){
37283             case "north":
37284                 return new r.North(cfg);
37285             case "south":
37286                 return new r.South(cfg);
37287             case "east":
37288                 return new r.East(cfg);
37289             case "west":
37290                 return new r.West(cfg);
37291             case "center":
37292                 return new r.Center(cfg);
37293         }
37294         throw 'Layout region "'+target+'" not supported.';
37295     }
37296     
37297     
37298 });
37299  /*
37300  * Based on:
37301  * Ext JS Library 1.1.1
37302  * Copyright(c) 2006-2007, Ext JS, LLC.
37303  *
37304  * Originally Released Under LGPL - original licence link has changed is not relivant.
37305  *
37306  * Fork - LGPL
37307  * <script type="text/javascript">
37308  */
37309  
37310 /**
37311  * @class Roo.bootstrap.layout.Basic
37312  * @extends Roo.util.Observable
37313  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37314  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37315  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37316  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37317  * @cfg {string}   region  the region that it inhabits..
37318  * @cfg {bool}   skipConfig skip config?
37319  * 
37320
37321  */
37322 Roo.bootstrap.layout.Basic = function(config){
37323     
37324     this.mgr = config.mgr;
37325     
37326     this.position = config.region;
37327     
37328     var skipConfig = config.skipConfig;
37329     
37330     this.events = {
37331         /**
37332          * @scope Roo.BasicLayoutRegion
37333          */
37334         
37335         /**
37336          * @event beforeremove
37337          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37338          * @param {Roo.LayoutRegion} this
37339          * @param {Roo.ContentPanel} panel The panel
37340          * @param {Object} e The cancel event object
37341          */
37342         "beforeremove" : true,
37343         /**
37344          * @event invalidated
37345          * Fires when the layout for this region is changed.
37346          * @param {Roo.LayoutRegion} this
37347          */
37348         "invalidated" : true,
37349         /**
37350          * @event visibilitychange
37351          * Fires when this region is shown or hidden 
37352          * @param {Roo.LayoutRegion} this
37353          * @param {Boolean} visibility true or false
37354          */
37355         "visibilitychange" : true,
37356         /**
37357          * @event paneladded
37358          * Fires when a panel is added. 
37359          * @param {Roo.LayoutRegion} this
37360          * @param {Roo.ContentPanel} panel The panel
37361          */
37362         "paneladded" : true,
37363         /**
37364          * @event panelremoved
37365          * Fires when a panel is removed. 
37366          * @param {Roo.LayoutRegion} this
37367          * @param {Roo.ContentPanel} panel The panel
37368          */
37369         "panelremoved" : true,
37370         /**
37371          * @event beforecollapse
37372          * Fires when this region before collapse.
37373          * @param {Roo.LayoutRegion} this
37374          */
37375         "beforecollapse" : true,
37376         /**
37377          * @event collapsed
37378          * Fires when this region is collapsed.
37379          * @param {Roo.LayoutRegion} this
37380          */
37381         "collapsed" : true,
37382         /**
37383          * @event expanded
37384          * Fires when this region is expanded.
37385          * @param {Roo.LayoutRegion} this
37386          */
37387         "expanded" : true,
37388         /**
37389          * @event slideshow
37390          * Fires when this region is slid into view.
37391          * @param {Roo.LayoutRegion} this
37392          */
37393         "slideshow" : true,
37394         /**
37395          * @event slidehide
37396          * Fires when this region slides out of view. 
37397          * @param {Roo.LayoutRegion} this
37398          */
37399         "slidehide" : true,
37400         /**
37401          * @event panelactivated
37402          * Fires when a panel is activated. 
37403          * @param {Roo.LayoutRegion} this
37404          * @param {Roo.ContentPanel} panel The activated panel
37405          */
37406         "panelactivated" : true,
37407         /**
37408          * @event resized
37409          * Fires when the user resizes this region. 
37410          * @param {Roo.LayoutRegion} this
37411          * @param {Number} newSize The new size (width for east/west, height for north/south)
37412          */
37413         "resized" : true
37414     };
37415     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37416     this.panels = new Roo.util.MixedCollection();
37417     this.panels.getKey = this.getPanelId.createDelegate(this);
37418     this.box = null;
37419     this.activePanel = null;
37420     // ensure listeners are added...
37421     
37422     if (config.listeners || config.events) {
37423         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37424             listeners : config.listeners || {},
37425             events : config.events || {}
37426         });
37427     }
37428     
37429     if(skipConfig !== true){
37430         this.applyConfig(config);
37431     }
37432 };
37433
37434 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37435 {
37436     getPanelId : function(p){
37437         return p.getId();
37438     },
37439     
37440     applyConfig : function(config){
37441         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37442         this.config = config;
37443         
37444     },
37445     
37446     /**
37447      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37448      * the width, for horizontal (north, south) the height.
37449      * @param {Number} newSize The new width or height
37450      */
37451     resizeTo : function(newSize){
37452         var el = this.el ? this.el :
37453                  (this.activePanel ? this.activePanel.getEl() : null);
37454         if(el){
37455             switch(this.position){
37456                 case "east":
37457                 case "west":
37458                     el.setWidth(newSize);
37459                     this.fireEvent("resized", this, newSize);
37460                 break;
37461                 case "north":
37462                 case "south":
37463                     el.setHeight(newSize);
37464                     this.fireEvent("resized", this, newSize);
37465                 break;                
37466             }
37467         }
37468     },
37469     
37470     getBox : function(){
37471         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37472     },
37473     
37474     getMargins : function(){
37475         return this.margins;
37476     },
37477     
37478     updateBox : function(box){
37479         this.box = box;
37480         var el = this.activePanel.getEl();
37481         el.dom.style.left = box.x + "px";
37482         el.dom.style.top = box.y + "px";
37483         this.activePanel.setSize(box.width, box.height);
37484     },
37485     
37486     /**
37487      * Returns the container element for this region.
37488      * @return {Roo.Element}
37489      */
37490     getEl : function(){
37491         return this.activePanel;
37492     },
37493     
37494     /**
37495      * Returns true if this region is currently visible.
37496      * @return {Boolean}
37497      */
37498     isVisible : function(){
37499         return this.activePanel ? true : false;
37500     },
37501     
37502     setActivePanel : function(panel){
37503         panel = this.getPanel(panel);
37504         if(this.activePanel && this.activePanel != panel){
37505             this.activePanel.setActiveState(false);
37506             this.activePanel.getEl().setLeftTop(-10000,-10000);
37507         }
37508         this.activePanel = panel;
37509         panel.setActiveState(true);
37510         if(this.box){
37511             panel.setSize(this.box.width, this.box.height);
37512         }
37513         this.fireEvent("panelactivated", this, panel);
37514         this.fireEvent("invalidated");
37515     },
37516     
37517     /**
37518      * Show the specified panel.
37519      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37520      * @return {Roo.ContentPanel} The shown panel or null
37521      */
37522     showPanel : function(panel){
37523         panel = this.getPanel(panel);
37524         if(panel){
37525             this.setActivePanel(panel);
37526         }
37527         return panel;
37528     },
37529     
37530     /**
37531      * Get the active panel for this region.
37532      * @return {Roo.ContentPanel} The active panel or null
37533      */
37534     getActivePanel : function(){
37535         return this.activePanel;
37536     },
37537     
37538     /**
37539      * Add the passed ContentPanel(s)
37540      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37541      * @return {Roo.ContentPanel} The panel added (if only one was added)
37542      */
37543     add : function(panel){
37544         if(arguments.length > 1){
37545             for(var i = 0, len = arguments.length; i < len; i++) {
37546                 this.add(arguments[i]);
37547             }
37548             return null;
37549         }
37550         if(this.hasPanel(panel)){
37551             this.showPanel(panel);
37552             return panel;
37553         }
37554         var el = panel.getEl();
37555         if(el.dom.parentNode != this.mgr.el.dom){
37556             this.mgr.el.dom.appendChild(el.dom);
37557         }
37558         if(panel.setRegion){
37559             panel.setRegion(this);
37560         }
37561         this.panels.add(panel);
37562         el.setStyle("position", "absolute");
37563         if(!panel.background){
37564             this.setActivePanel(panel);
37565             if(this.config.initialSize && this.panels.getCount()==1){
37566                 this.resizeTo(this.config.initialSize);
37567             }
37568         }
37569         this.fireEvent("paneladded", this, panel);
37570         return panel;
37571     },
37572     
37573     /**
37574      * Returns true if the panel is in this region.
37575      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37576      * @return {Boolean}
37577      */
37578     hasPanel : function(panel){
37579         if(typeof panel == "object"){ // must be panel obj
37580             panel = panel.getId();
37581         }
37582         return this.getPanel(panel) ? true : false;
37583     },
37584     
37585     /**
37586      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37587      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37588      * @param {Boolean} preservePanel Overrides the config preservePanel option
37589      * @return {Roo.ContentPanel} The panel that was removed
37590      */
37591     remove : function(panel, preservePanel){
37592         panel = this.getPanel(panel);
37593         if(!panel){
37594             return null;
37595         }
37596         var e = {};
37597         this.fireEvent("beforeremove", this, panel, e);
37598         if(e.cancel === true){
37599             return null;
37600         }
37601         var panelId = panel.getId();
37602         this.panels.removeKey(panelId);
37603         return panel;
37604     },
37605     
37606     /**
37607      * Returns the panel specified or null if it's not in this region.
37608      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37609      * @return {Roo.ContentPanel}
37610      */
37611     getPanel : function(id){
37612         if(typeof id == "object"){ // must be panel obj
37613             return id;
37614         }
37615         return this.panels.get(id);
37616     },
37617     
37618     /**
37619      * Returns this regions position (north/south/east/west/center).
37620      * @return {String} 
37621      */
37622     getPosition: function(){
37623         return this.position;    
37624     }
37625 });/*
37626  * Based on:
37627  * Ext JS Library 1.1.1
37628  * Copyright(c) 2006-2007, Ext JS, LLC.
37629  *
37630  * Originally Released Under LGPL - original licence link has changed is not relivant.
37631  *
37632  * Fork - LGPL
37633  * <script type="text/javascript">
37634  */
37635  
37636 /**
37637  * @class Roo.bootstrap.layout.Region
37638  * @extends Roo.bootstrap.layout.Basic
37639  * This class represents a region in a layout manager.
37640  
37641  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37642  * @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})
37643  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37644  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37645  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37646  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37647  * @cfg {String}    title           The title for the region (overrides panel titles)
37648  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37649  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37650  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37651  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37652  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37653  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37654  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37655  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37656  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37657  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37658
37659  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37660  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37661  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37662  * @cfg {Number}    width           For East/West panels
37663  * @cfg {Number}    height          For North/South panels
37664  * @cfg {Boolean}   split           To show the splitter
37665  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37666  * 
37667  * @cfg {string}   cls             Extra CSS classes to add to region
37668  * 
37669  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37670  * @cfg {string}   region  the region that it inhabits..
37671  *
37672
37673  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37674  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37675
37676  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37677  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37678  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37679  */
37680 Roo.bootstrap.layout.Region = function(config)
37681 {
37682     this.applyConfig(config);
37683
37684     var mgr = config.mgr;
37685     var pos = config.region;
37686     config.skipConfig = true;
37687     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37688     
37689     if (mgr.el) {
37690         this.onRender(mgr.el);   
37691     }
37692      
37693     this.visible = true;
37694     this.collapsed = false;
37695     this.unrendered_panels = [];
37696 };
37697
37698 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37699
37700     position: '', // set by wrapper (eg. north/south etc..)
37701     unrendered_panels : null,  // unrendered panels.
37702     
37703     tabPosition : false,
37704     
37705     mgr: false, // points to 'Border'
37706     
37707     
37708     createBody : function(){
37709         /** This region's body element 
37710         * @type Roo.Element */
37711         this.bodyEl = this.el.createChild({
37712                 tag: "div",
37713                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37714         });
37715     },
37716
37717     onRender: function(ctr, pos)
37718     {
37719         var dh = Roo.DomHelper;
37720         /** This region's container element 
37721         * @type Roo.Element */
37722         this.el = dh.append(ctr.dom, {
37723                 tag: "div",
37724                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37725             }, true);
37726         /** This region's title element 
37727         * @type Roo.Element */
37728     
37729         this.titleEl = dh.append(this.el.dom,  {
37730                 tag: "div",
37731                 unselectable: "on",
37732                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37733                 children:[
37734                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37735                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37736                 ]
37737             }, true);
37738         
37739         this.titleEl.enableDisplayMode();
37740         /** This region's title text element 
37741         * @type HTMLElement */
37742         this.titleTextEl = this.titleEl.dom.firstChild;
37743         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37744         /*
37745         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37746         this.closeBtn.enableDisplayMode();
37747         this.closeBtn.on("click", this.closeClicked, this);
37748         this.closeBtn.hide();
37749     */
37750         this.createBody(this.config);
37751         if(this.config.hideWhenEmpty){
37752             this.hide();
37753             this.on("paneladded", this.validateVisibility, this);
37754             this.on("panelremoved", this.validateVisibility, this);
37755         }
37756         if(this.autoScroll){
37757             this.bodyEl.setStyle("overflow", "auto");
37758         }else{
37759             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37760         }
37761         //if(c.titlebar !== false){
37762             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37763                 this.titleEl.hide();
37764             }else{
37765                 this.titleEl.show();
37766                 if(this.config.title){
37767                     this.titleTextEl.innerHTML = this.config.title;
37768                 }
37769             }
37770         //}
37771         if(this.config.collapsed){
37772             this.collapse(true);
37773         }
37774         if(this.config.hidden){
37775             this.hide();
37776         }
37777         
37778         if (this.unrendered_panels && this.unrendered_panels.length) {
37779             for (var i =0;i< this.unrendered_panels.length; i++) {
37780                 this.add(this.unrendered_panels[i]);
37781             }
37782             this.unrendered_panels = null;
37783             
37784         }
37785         
37786     },
37787     
37788     applyConfig : function(c)
37789     {
37790         /*
37791          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37792             var dh = Roo.DomHelper;
37793             if(c.titlebar !== false){
37794                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37795                 this.collapseBtn.on("click", this.collapse, this);
37796                 this.collapseBtn.enableDisplayMode();
37797                 /*
37798                 if(c.showPin === true || this.showPin){
37799                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37800                     this.stickBtn.enableDisplayMode();
37801                     this.stickBtn.on("click", this.expand, this);
37802                     this.stickBtn.hide();
37803                 }
37804                 
37805             }
37806             */
37807             /** This region's collapsed element
37808             * @type Roo.Element */
37809             /*
37810              *
37811             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37812                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37813             ]}, true);
37814             
37815             if(c.floatable !== false){
37816                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37817                this.collapsedEl.on("click", this.collapseClick, this);
37818             }
37819
37820             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37821                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37822                    id: "message", unselectable: "on", style:{"float":"left"}});
37823                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37824              }
37825             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37826             this.expandBtn.on("click", this.expand, this);
37827             
37828         }
37829         
37830         if(this.collapseBtn){
37831             this.collapseBtn.setVisible(c.collapsible == true);
37832         }
37833         
37834         this.cmargins = c.cmargins || this.cmargins ||
37835                          (this.position == "west" || this.position == "east" ?
37836                              {top: 0, left: 2, right:2, bottom: 0} :
37837                              {top: 2, left: 0, right:0, bottom: 2});
37838         */
37839         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37840         
37841         
37842         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37843         
37844         this.autoScroll = c.autoScroll || false;
37845         
37846         
37847        
37848         
37849         this.duration = c.duration || .30;
37850         this.slideDuration = c.slideDuration || .45;
37851         this.config = c;
37852        
37853     },
37854     /**
37855      * Returns true if this region is currently visible.
37856      * @return {Boolean}
37857      */
37858     isVisible : function(){
37859         return this.visible;
37860     },
37861
37862     /**
37863      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
37864      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
37865      */
37866     //setCollapsedTitle : function(title){
37867     //    title = title || "&#160;";
37868      //   if(this.collapsedTitleTextEl){
37869       //      this.collapsedTitleTextEl.innerHTML = title;
37870        // }
37871     //},
37872
37873     getBox : function(){
37874         var b;
37875       //  if(!this.collapsed){
37876             b = this.el.getBox(false, true);
37877        // }else{
37878           //  b = this.collapsedEl.getBox(false, true);
37879         //}
37880         return b;
37881     },
37882
37883     getMargins : function(){
37884         return this.margins;
37885         //return this.collapsed ? this.cmargins : this.margins;
37886     },
37887 /*
37888     highlight : function(){
37889         this.el.addClass("x-layout-panel-dragover");
37890     },
37891
37892     unhighlight : function(){
37893         this.el.removeClass("x-layout-panel-dragover");
37894     },
37895 */
37896     updateBox : function(box)
37897     {
37898         if (!this.bodyEl) {
37899             return; // not rendered yet..
37900         }
37901         
37902         this.box = box;
37903         if(!this.collapsed){
37904             this.el.dom.style.left = box.x + "px";
37905             this.el.dom.style.top = box.y + "px";
37906             this.updateBody(box.width, box.height);
37907         }else{
37908             this.collapsedEl.dom.style.left = box.x + "px";
37909             this.collapsedEl.dom.style.top = box.y + "px";
37910             this.collapsedEl.setSize(box.width, box.height);
37911         }
37912         if(this.tabs){
37913             this.tabs.autoSizeTabs();
37914         }
37915     },
37916
37917     updateBody : function(w, h)
37918     {
37919         if(w !== null){
37920             this.el.setWidth(w);
37921             w -= this.el.getBorderWidth("rl");
37922             if(this.config.adjustments){
37923                 w += this.config.adjustments[0];
37924             }
37925         }
37926         if(h !== null && h > 0){
37927             this.el.setHeight(h);
37928             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
37929             h -= this.el.getBorderWidth("tb");
37930             if(this.config.adjustments){
37931                 h += this.config.adjustments[1];
37932             }
37933             this.bodyEl.setHeight(h);
37934             if(this.tabs){
37935                 h = this.tabs.syncHeight(h);
37936             }
37937         }
37938         if(this.panelSize){
37939             w = w !== null ? w : this.panelSize.width;
37940             h = h !== null ? h : this.panelSize.height;
37941         }
37942         if(this.activePanel){
37943             var el = this.activePanel.getEl();
37944             w = w !== null ? w : el.getWidth();
37945             h = h !== null ? h : el.getHeight();
37946             this.panelSize = {width: w, height: h};
37947             this.activePanel.setSize(w, h);
37948         }
37949         if(Roo.isIE && this.tabs){
37950             this.tabs.el.repaint();
37951         }
37952     },
37953
37954     /**
37955      * Returns the container element for this region.
37956      * @return {Roo.Element}
37957      */
37958     getEl : function(){
37959         return this.el;
37960     },
37961
37962     /**
37963      * Hides this region.
37964      */
37965     hide : function(){
37966         //if(!this.collapsed){
37967             this.el.dom.style.left = "-2000px";
37968             this.el.hide();
37969         //}else{
37970          //   this.collapsedEl.dom.style.left = "-2000px";
37971          //   this.collapsedEl.hide();
37972        // }
37973         this.visible = false;
37974         this.fireEvent("visibilitychange", this, false);
37975     },
37976
37977     /**
37978      * Shows this region if it was previously hidden.
37979      */
37980     show : function(){
37981         //if(!this.collapsed){
37982             this.el.show();
37983         //}else{
37984         //    this.collapsedEl.show();
37985        // }
37986         this.visible = true;
37987         this.fireEvent("visibilitychange", this, true);
37988     },
37989 /*
37990     closeClicked : function(){
37991         if(this.activePanel){
37992             this.remove(this.activePanel);
37993         }
37994     },
37995
37996     collapseClick : function(e){
37997         if(this.isSlid){
37998            e.stopPropagation();
37999            this.slideIn();
38000         }else{
38001            e.stopPropagation();
38002            this.slideOut();
38003         }
38004     },
38005 */
38006     /**
38007      * Collapses this region.
38008      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38009      */
38010     /*
38011     collapse : function(skipAnim, skipCheck = false){
38012         if(this.collapsed) {
38013             return;
38014         }
38015         
38016         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38017             
38018             this.collapsed = true;
38019             if(this.split){
38020                 this.split.el.hide();
38021             }
38022             if(this.config.animate && skipAnim !== true){
38023                 this.fireEvent("invalidated", this);
38024                 this.animateCollapse();
38025             }else{
38026                 this.el.setLocation(-20000,-20000);
38027                 this.el.hide();
38028                 this.collapsedEl.show();
38029                 this.fireEvent("collapsed", this);
38030                 this.fireEvent("invalidated", this);
38031             }
38032         }
38033         
38034     },
38035 */
38036     animateCollapse : function(){
38037         // overridden
38038     },
38039
38040     /**
38041      * Expands this region if it was previously collapsed.
38042      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38043      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38044      */
38045     /*
38046     expand : function(e, skipAnim){
38047         if(e) {
38048             e.stopPropagation();
38049         }
38050         if(!this.collapsed || this.el.hasActiveFx()) {
38051             return;
38052         }
38053         if(this.isSlid){
38054             this.afterSlideIn();
38055             skipAnim = true;
38056         }
38057         this.collapsed = false;
38058         if(this.config.animate && skipAnim !== true){
38059             this.animateExpand();
38060         }else{
38061             this.el.show();
38062             if(this.split){
38063                 this.split.el.show();
38064             }
38065             this.collapsedEl.setLocation(-2000,-2000);
38066             this.collapsedEl.hide();
38067             this.fireEvent("invalidated", this);
38068             this.fireEvent("expanded", this);
38069         }
38070     },
38071 */
38072     animateExpand : function(){
38073         // overridden
38074     },
38075
38076     initTabs : function()
38077     {
38078         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38079         
38080         var ts = new Roo.bootstrap.panel.Tabs({
38081             el: this.bodyEl.dom,
38082             region : this,
38083             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38084             disableTooltips: this.config.disableTabTips,
38085             toolbar : this.config.toolbar
38086         });
38087         
38088         if(this.config.hideTabs){
38089             ts.stripWrap.setDisplayed(false);
38090         }
38091         this.tabs = ts;
38092         ts.resizeTabs = this.config.resizeTabs === true;
38093         ts.minTabWidth = this.config.minTabWidth || 40;
38094         ts.maxTabWidth = this.config.maxTabWidth || 250;
38095         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38096         ts.monitorResize = false;
38097         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38098         ts.bodyEl.addClass('roo-layout-tabs-body');
38099         this.panels.each(this.initPanelAsTab, this);
38100     },
38101
38102     initPanelAsTab : function(panel){
38103         var ti = this.tabs.addTab(
38104             panel.getEl().id,
38105             panel.getTitle(),
38106             null,
38107             this.config.closeOnTab && panel.isClosable(),
38108             panel.tpl
38109         );
38110         if(panel.tabTip !== undefined){
38111             ti.setTooltip(panel.tabTip);
38112         }
38113         ti.on("activate", function(){
38114               this.setActivePanel(panel);
38115         }, this);
38116         
38117         if(this.config.closeOnTab){
38118             ti.on("beforeclose", function(t, e){
38119                 e.cancel = true;
38120                 this.remove(panel);
38121             }, this);
38122         }
38123         
38124         panel.tabItem = ti;
38125         
38126         return ti;
38127     },
38128
38129     updatePanelTitle : function(panel, title)
38130     {
38131         if(this.activePanel == panel){
38132             this.updateTitle(title);
38133         }
38134         if(this.tabs){
38135             var ti = this.tabs.getTab(panel.getEl().id);
38136             ti.setText(title);
38137             if(panel.tabTip !== undefined){
38138                 ti.setTooltip(panel.tabTip);
38139             }
38140         }
38141     },
38142
38143     updateTitle : function(title){
38144         if(this.titleTextEl && !this.config.title){
38145             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38146         }
38147     },
38148
38149     setActivePanel : function(panel)
38150     {
38151         panel = this.getPanel(panel);
38152         if(this.activePanel && this.activePanel != panel){
38153             if(this.activePanel.setActiveState(false) === false){
38154                 return;
38155             }
38156         }
38157         this.activePanel = panel;
38158         panel.setActiveState(true);
38159         if(this.panelSize){
38160             panel.setSize(this.panelSize.width, this.panelSize.height);
38161         }
38162         if(this.closeBtn){
38163             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38164         }
38165         this.updateTitle(panel.getTitle());
38166         if(this.tabs){
38167             this.fireEvent("invalidated", this);
38168         }
38169         this.fireEvent("panelactivated", this, panel);
38170     },
38171
38172     /**
38173      * Shows the specified panel.
38174      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38175      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38176      */
38177     showPanel : function(panel)
38178     {
38179         panel = this.getPanel(panel);
38180         if(panel){
38181             if(this.tabs){
38182                 var tab = this.tabs.getTab(panel.getEl().id);
38183                 if(tab.isHidden()){
38184                     this.tabs.unhideTab(tab.id);
38185                 }
38186                 tab.activate();
38187             }else{
38188                 this.setActivePanel(panel);
38189             }
38190         }
38191         return panel;
38192     },
38193
38194     /**
38195      * Get the active panel for this region.
38196      * @return {Roo.ContentPanel} The active panel or null
38197      */
38198     getActivePanel : function(){
38199         return this.activePanel;
38200     },
38201
38202     validateVisibility : function(){
38203         if(this.panels.getCount() < 1){
38204             this.updateTitle("&#160;");
38205             this.closeBtn.hide();
38206             this.hide();
38207         }else{
38208             if(!this.isVisible()){
38209                 this.show();
38210             }
38211         }
38212     },
38213
38214     /**
38215      * Adds the passed ContentPanel(s) to this region.
38216      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38217      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38218      */
38219     add : function(panel)
38220     {
38221         if(arguments.length > 1){
38222             for(var i = 0, len = arguments.length; i < len; i++) {
38223                 this.add(arguments[i]);
38224             }
38225             return null;
38226         }
38227         
38228         // if we have not been rendered yet, then we can not really do much of this..
38229         if (!this.bodyEl) {
38230             this.unrendered_panels.push(panel);
38231             return panel;
38232         }
38233         
38234         
38235         
38236         
38237         if(this.hasPanel(panel)){
38238             this.showPanel(panel);
38239             return panel;
38240         }
38241         panel.setRegion(this);
38242         this.panels.add(panel);
38243        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38244             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38245             // and hide them... ???
38246             this.bodyEl.dom.appendChild(panel.getEl().dom);
38247             if(panel.background !== true){
38248                 this.setActivePanel(panel);
38249             }
38250             this.fireEvent("paneladded", this, panel);
38251             return panel;
38252         }
38253         */
38254         if(!this.tabs){
38255             this.initTabs();
38256         }else{
38257             this.initPanelAsTab(panel);
38258         }
38259         
38260         
38261         if(panel.background !== true){
38262             this.tabs.activate(panel.getEl().id);
38263         }
38264         this.fireEvent("paneladded", this, panel);
38265         return panel;
38266     },
38267
38268     /**
38269      * Hides the tab for the specified panel.
38270      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38271      */
38272     hidePanel : function(panel){
38273         if(this.tabs && (panel = this.getPanel(panel))){
38274             this.tabs.hideTab(panel.getEl().id);
38275         }
38276     },
38277
38278     /**
38279      * Unhides the tab for a previously hidden panel.
38280      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38281      */
38282     unhidePanel : function(panel){
38283         if(this.tabs && (panel = this.getPanel(panel))){
38284             this.tabs.unhideTab(panel.getEl().id);
38285         }
38286     },
38287
38288     clearPanels : function(){
38289         while(this.panels.getCount() > 0){
38290              this.remove(this.panels.first());
38291         }
38292     },
38293
38294     /**
38295      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38296      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38297      * @param {Boolean} preservePanel Overrides the config preservePanel option
38298      * @return {Roo.ContentPanel} The panel that was removed
38299      */
38300     remove : function(panel, preservePanel)
38301     {
38302         panel = this.getPanel(panel);
38303         if(!panel){
38304             return null;
38305         }
38306         var e = {};
38307         this.fireEvent("beforeremove", this, panel, e);
38308         if(e.cancel === true){
38309             return null;
38310         }
38311         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38312         var panelId = panel.getId();
38313         this.panels.removeKey(panelId);
38314         if(preservePanel){
38315             document.body.appendChild(panel.getEl().dom);
38316         }
38317         if(this.tabs){
38318             this.tabs.removeTab(panel.getEl().id);
38319         }else if (!preservePanel){
38320             this.bodyEl.dom.removeChild(panel.getEl().dom);
38321         }
38322         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38323             var p = this.panels.first();
38324             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38325             tempEl.appendChild(p.getEl().dom);
38326             this.bodyEl.update("");
38327             this.bodyEl.dom.appendChild(p.getEl().dom);
38328             tempEl = null;
38329             this.updateTitle(p.getTitle());
38330             this.tabs = null;
38331             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38332             this.setActivePanel(p);
38333         }
38334         panel.setRegion(null);
38335         if(this.activePanel == panel){
38336             this.activePanel = null;
38337         }
38338         if(this.config.autoDestroy !== false && preservePanel !== true){
38339             try{panel.destroy();}catch(e){}
38340         }
38341         this.fireEvent("panelremoved", this, panel);
38342         return panel;
38343     },
38344
38345     /**
38346      * Returns the TabPanel component used by this region
38347      * @return {Roo.TabPanel}
38348      */
38349     getTabs : function(){
38350         return this.tabs;
38351     },
38352
38353     createTool : function(parentEl, className){
38354         var btn = Roo.DomHelper.append(parentEl, {
38355             tag: "div",
38356             cls: "x-layout-tools-button",
38357             children: [ {
38358                 tag: "div",
38359                 cls: "roo-layout-tools-button-inner " + className,
38360                 html: "&#160;"
38361             }]
38362         }, true);
38363         btn.addClassOnOver("roo-layout-tools-button-over");
38364         return btn;
38365     }
38366 });/*
38367  * Based on:
38368  * Ext JS Library 1.1.1
38369  * Copyright(c) 2006-2007, Ext JS, LLC.
38370  *
38371  * Originally Released Under LGPL - original licence link has changed is not relivant.
38372  *
38373  * Fork - LGPL
38374  * <script type="text/javascript">
38375  */
38376  
38377
38378
38379 /**
38380  * @class Roo.SplitLayoutRegion
38381  * @extends Roo.LayoutRegion
38382  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38383  */
38384 Roo.bootstrap.layout.Split = function(config){
38385     this.cursor = config.cursor;
38386     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38387 };
38388
38389 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38390 {
38391     splitTip : "Drag to resize.",
38392     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38393     useSplitTips : false,
38394
38395     applyConfig : function(config){
38396         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38397     },
38398     
38399     onRender : function(ctr,pos) {
38400         
38401         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38402         if(!this.config.split){
38403             return;
38404         }
38405         if(!this.split){
38406             
38407             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38408                             tag: "div",
38409                             id: this.el.id + "-split",
38410                             cls: "roo-layout-split roo-layout-split-"+this.position,
38411                             html: "&#160;"
38412             });
38413             /** The SplitBar for this region 
38414             * @type Roo.SplitBar */
38415             // does not exist yet...
38416             Roo.log([this.position, this.orientation]);
38417             
38418             this.split = new Roo.bootstrap.SplitBar({
38419                 dragElement : splitEl,
38420                 resizingElement: this.el,
38421                 orientation : this.orientation
38422             });
38423             
38424             this.split.on("moved", this.onSplitMove, this);
38425             this.split.useShim = this.config.useShim === true;
38426             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38427             if(this.useSplitTips){
38428                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38429             }
38430             //if(config.collapsible){
38431             //    this.split.el.on("dblclick", this.collapse,  this);
38432             //}
38433         }
38434         if(typeof this.config.minSize != "undefined"){
38435             this.split.minSize = this.config.minSize;
38436         }
38437         if(typeof this.config.maxSize != "undefined"){
38438             this.split.maxSize = this.config.maxSize;
38439         }
38440         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38441             this.hideSplitter();
38442         }
38443         
38444     },
38445
38446     getHMaxSize : function(){
38447          var cmax = this.config.maxSize || 10000;
38448          var center = this.mgr.getRegion("center");
38449          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38450     },
38451
38452     getVMaxSize : function(){
38453          var cmax = this.config.maxSize || 10000;
38454          var center = this.mgr.getRegion("center");
38455          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38456     },
38457
38458     onSplitMove : function(split, newSize){
38459         this.fireEvent("resized", this, newSize);
38460     },
38461     
38462     /** 
38463      * Returns the {@link Roo.SplitBar} for this region.
38464      * @return {Roo.SplitBar}
38465      */
38466     getSplitBar : function(){
38467         return this.split;
38468     },
38469     
38470     hide : function(){
38471         this.hideSplitter();
38472         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38473     },
38474
38475     hideSplitter : function(){
38476         if(this.split){
38477             this.split.el.setLocation(-2000,-2000);
38478             this.split.el.hide();
38479         }
38480     },
38481
38482     show : function(){
38483         if(this.split){
38484             this.split.el.show();
38485         }
38486         Roo.bootstrap.layout.Split.superclass.show.call(this);
38487     },
38488     
38489     beforeSlide: function(){
38490         if(Roo.isGecko){// firefox overflow auto bug workaround
38491             this.bodyEl.clip();
38492             if(this.tabs) {
38493                 this.tabs.bodyEl.clip();
38494             }
38495             if(this.activePanel){
38496                 this.activePanel.getEl().clip();
38497                 
38498                 if(this.activePanel.beforeSlide){
38499                     this.activePanel.beforeSlide();
38500                 }
38501             }
38502         }
38503     },
38504     
38505     afterSlide : function(){
38506         if(Roo.isGecko){// firefox overflow auto bug workaround
38507             this.bodyEl.unclip();
38508             if(this.tabs) {
38509                 this.tabs.bodyEl.unclip();
38510             }
38511             if(this.activePanel){
38512                 this.activePanel.getEl().unclip();
38513                 if(this.activePanel.afterSlide){
38514                     this.activePanel.afterSlide();
38515                 }
38516             }
38517         }
38518     },
38519
38520     initAutoHide : function(){
38521         if(this.autoHide !== false){
38522             if(!this.autoHideHd){
38523                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38524                 this.autoHideHd = {
38525                     "mouseout": function(e){
38526                         if(!e.within(this.el, true)){
38527                             st.delay(500);
38528                         }
38529                     },
38530                     "mouseover" : function(e){
38531                         st.cancel();
38532                     },
38533                     scope : this
38534                 };
38535             }
38536             this.el.on(this.autoHideHd);
38537         }
38538     },
38539
38540     clearAutoHide : function(){
38541         if(this.autoHide !== false){
38542             this.el.un("mouseout", this.autoHideHd.mouseout);
38543             this.el.un("mouseover", this.autoHideHd.mouseover);
38544         }
38545     },
38546
38547     clearMonitor : function(){
38548         Roo.get(document).un("click", this.slideInIf, this);
38549     },
38550
38551     // these names are backwards but not changed for compat
38552     slideOut : function(){
38553         if(this.isSlid || this.el.hasActiveFx()){
38554             return;
38555         }
38556         this.isSlid = true;
38557         if(this.collapseBtn){
38558             this.collapseBtn.hide();
38559         }
38560         this.closeBtnState = this.closeBtn.getStyle('display');
38561         this.closeBtn.hide();
38562         if(this.stickBtn){
38563             this.stickBtn.show();
38564         }
38565         this.el.show();
38566         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38567         this.beforeSlide();
38568         this.el.setStyle("z-index", 10001);
38569         this.el.slideIn(this.getSlideAnchor(), {
38570             callback: function(){
38571                 this.afterSlide();
38572                 this.initAutoHide();
38573                 Roo.get(document).on("click", this.slideInIf, this);
38574                 this.fireEvent("slideshow", this);
38575             },
38576             scope: this,
38577             block: true
38578         });
38579     },
38580
38581     afterSlideIn : function(){
38582         this.clearAutoHide();
38583         this.isSlid = false;
38584         this.clearMonitor();
38585         this.el.setStyle("z-index", "");
38586         if(this.collapseBtn){
38587             this.collapseBtn.show();
38588         }
38589         this.closeBtn.setStyle('display', this.closeBtnState);
38590         if(this.stickBtn){
38591             this.stickBtn.hide();
38592         }
38593         this.fireEvent("slidehide", this);
38594     },
38595
38596     slideIn : function(cb){
38597         if(!this.isSlid || this.el.hasActiveFx()){
38598             Roo.callback(cb);
38599             return;
38600         }
38601         this.isSlid = false;
38602         this.beforeSlide();
38603         this.el.slideOut(this.getSlideAnchor(), {
38604             callback: function(){
38605                 this.el.setLeftTop(-10000, -10000);
38606                 this.afterSlide();
38607                 this.afterSlideIn();
38608                 Roo.callback(cb);
38609             },
38610             scope: this,
38611             block: true
38612         });
38613     },
38614     
38615     slideInIf : function(e){
38616         if(!e.within(this.el)){
38617             this.slideIn();
38618         }
38619     },
38620
38621     animateCollapse : function(){
38622         this.beforeSlide();
38623         this.el.setStyle("z-index", 20000);
38624         var anchor = this.getSlideAnchor();
38625         this.el.slideOut(anchor, {
38626             callback : function(){
38627                 this.el.setStyle("z-index", "");
38628                 this.collapsedEl.slideIn(anchor, {duration:.3});
38629                 this.afterSlide();
38630                 this.el.setLocation(-10000,-10000);
38631                 this.el.hide();
38632                 this.fireEvent("collapsed", this);
38633             },
38634             scope: this,
38635             block: true
38636         });
38637     },
38638
38639     animateExpand : function(){
38640         this.beforeSlide();
38641         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38642         this.el.setStyle("z-index", 20000);
38643         this.collapsedEl.hide({
38644             duration:.1
38645         });
38646         this.el.slideIn(this.getSlideAnchor(), {
38647             callback : function(){
38648                 this.el.setStyle("z-index", "");
38649                 this.afterSlide();
38650                 if(this.split){
38651                     this.split.el.show();
38652                 }
38653                 this.fireEvent("invalidated", this);
38654                 this.fireEvent("expanded", this);
38655             },
38656             scope: this,
38657             block: true
38658         });
38659     },
38660
38661     anchors : {
38662         "west" : "left",
38663         "east" : "right",
38664         "north" : "top",
38665         "south" : "bottom"
38666     },
38667
38668     sanchors : {
38669         "west" : "l",
38670         "east" : "r",
38671         "north" : "t",
38672         "south" : "b"
38673     },
38674
38675     canchors : {
38676         "west" : "tl-tr",
38677         "east" : "tr-tl",
38678         "north" : "tl-bl",
38679         "south" : "bl-tl"
38680     },
38681
38682     getAnchor : function(){
38683         return this.anchors[this.position];
38684     },
38685
38686     getCollapseAnchor : function(){
38687         return this.canchors[this.position];
38688     },
38689
38690     getSlideAnchor : function(){
38691         return this.sanchors[this.position];
38692     },
38693
38694     getAlignAdj : function(){
38695         var cm = this.cmargins;
38696         switch(this.position){
38697             case "west":
38698                 return [0, 0];
38699             break;
38700             case "east":
38701                 return [0, 0];
38702             break;
38703             case "north":
38704                 return [0, 0];
38705             break;
38706             case "south":
38707                 return [0, 0];
38708             break;
38709         }
38710     },
38711
38712     getExpandAdj : function(){
38713         var c = this.collapsedEl, cm = this.cmargins;
38714         switch(this.position){
38715             case "west":
38716                 return [-(cm.right+c.getWidth()+cm.left), 0];
38717             break;
38718             case "east":
38719                 return [cm.right+c.getWidth()+cm.left, 0];
38720             break;
38721             case "north":
38722                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38723             break;
38724             case "south":
38725                 return [0, cm.top+cm.bottom+c.getHeight()];
38726             break;
38727         }
38728     }
38729 });/*
38730  * Based on:
38731  * Ext JS Library 1.1.1
38732  * Copyright(c) 2006-2007, Ext JS, LLC.
38733  *
38734  * Originally Released Under LGPL - original licence link has changed is not relivant.
38735  *
38736  * Fork - LGPL
38737  * <script type="text/javascript">
38738  */
38739 /*
38740  * These classes are private internal classes
38741  */
38742 Roo.bootstrap.layout.Center = function(config){
38743     config.region = "center";
38744     Roo.bootstrap.layout.Region.call(this, config);
38745     this.visible = true;
38746     this.minWidth = config.minWidth || 20;
38747     this.minHeight = config.minHeight || 20;
38748 };
38749
38750 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38751     hide : function(){
38752         // center panel can't be hidden
38753     },
38754     
38755     show : function(){
38756         // center panel can't be hidden
38757     },
38758     
38759     getMinWidth: function(){
38760         return this.minWidth;
38761     },
38762     
38763     getMinHeight: function(){
38764         return this.minHeight;
38765     }
38766 });
38767
38768
38769
38770
38771  
38772
38773
38774
38775
38776
38777
38778 Roo.bootstrap.layout.North = function(config)
38779 {
38780     config.region = 'north';
38781     config.cursor = 'n-resize';
38782     
38783     Roo.bootstrap.layout.Split.call(this, config);
38784     
38785     
38786     if(this.split){
38787         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38788         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38789         this.split.el.addClass("roo-layout-split-v");
38790     }
38791     var size = config.initialSize || config.height;
38792     if(typeof size != "undefined"){
38793         this.el.setHeight(size);
38794     }
38795 };
38796 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38797 {
38798     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38799     
38800     
38801     
38802     getBox : function(){
38803         if(this.collapsed){
38804             return this.collapsedEl.getBox();
38805         }
38806         var box = this.el.getBox();
38807         if(this.split){
38808             box.height += this.split.el.getHeight();
38809         }
38810         return box;
38811     },
38812     
38813     updateBox : function(box){
38814         if(this.split && !this.collapsed){
38815             box.height -= this.split.el.getHeight();
38816             this.split.el.setLeft(box.x);
38817             this.split.el.setTop(box.y+box.height);
38818             this.split.el.setWidth(box.width);
38819         }
38820         if(this.collapsed){
38821             this.updateBody(box.width, null);
38822         }
38823         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38824     }
38825 });
38826
38827
38828
38829
38830
38831 Roo.bootstrap.layout.South = function(config){
38832     config.region = 'south';
38833     config.cursor = 's-resize';
38834     Roo.bootstrap.layout.Split.call(this, config);
38835     if(this.split){
38836         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38837         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38838         this.split.el.addClass("roo-layout-split-v");
38839     }
38840     var size = config.initialSize || config.height;
38841     if(typeof size != "undefined"){
38842         this.el.setHeight(size);
38843     }
38844 };
38845
38846 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
38847     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38848     getBox : function(){
38849         if(this.collapsed){
38850             return this.collapsedEl.getBox();
38851         }
38852         var box = this.el.getBox();
38853         if(this.split){
38854             var sh = this.split.el.getHeight();
38855             box.height += sh;
38856             box.y -= sh;
38857         }
38858         return box;
38859     },
38860     
38861     updateBox : function(box){
38862         if(this.split && !this.collapsed){
38863             var sh = this.split.el.getHeight();
38864             box.height -= sh;
38865             box.y += sh;
38866             this.split.el.setLeft(box.x);
38867             this.split.el.setTop(box.y-sh);
38868             this.split.el.setWidth(box.width);
38869         }
38870         if(this.collapsed){
38871             this.updateBody(box.width, null);
38872         }
38873         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38874     }
38875 });
38876
38877 Roo.bootstrap.layout.East = function(config){
38878     config.region = "east";
38879     config.cursor = "e-resize";
38880     Roo.bootstrap.layout.Split.call(this, config);
38881     if(this.split){
38882         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
38883         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38884         this.split.el.addClass("roo-layout-split-h");
38885     }
38886     var size = config.initialSize || config.width;
38887     if(typeof size != "undefined"){
38888         this.el.setWidth(size);
38889     }
38890 };
38891 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
38892     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38893     getBox : function(){
38894         if(this.collapsed){
38895             return this.collapsedEl.getBox();
38896         }
38897         var box = this.el.getBox();
38898         if(this.split){
38899             var sw = this.split.el.getWidth();
38900             box.width += sw;
38901             box.x -= sw;
38902         }
38903         return box;
38904     },
38905
38906     updateBox : function(box){
38907         if(this.split && !this.collapsed){
38908             var sw = this.split.el.getWidth();
38909             box.width -= sw;
38910             this.split.el.setLeft(box.x);
38911             this.split.el.setTop(box.y);
38912             this.split.el.setHeight(box.height);
38913             box.x += sw;
38914         }
38915         if(this.collapsed){
38916             this.updateBody(null, box.height);
38917         }
38918         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38919     }
38920 });
38921
38922 Roo.bootstrap.layout.West = function(config){
38923     config.region = "west";
38924     config.cursor = "w-resize";
38925     
38926     Roo.bootstrap.layout.Split.call(this, config);
38927     if(this.split){
38928         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
38929         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
38930         this.split.el.addClass("roo-layout-split-h");
38931     }
38932     
38933 };
38934 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
38935     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
38936     
38937     onRender: function(ctr, pos)
38938     {
38939         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
38940         var size = this.config.initialSize || this.config.width;
38941         if(typeof size != "undefined"){
38942             this.el.setWidth(size);
38943         }
38944     },
38945     
38946     getBox : function(){
38947         if(this.collapsed){
38948             return this.collapsedEl.getBox();
38949         }
38950         var box = this.el.getBox();
38951         if(this.split){
38952             box.width += this.split.el.getWidth();
38953         }
38954         return box;
38955     },
38956     
38957     updateBox : function(box){
38958         if(this.split && !this.collapsed){
38959             var sw = this.split.el.getWidth();
38960             box.width -= sw;
38961             this.split.el.setLeft(box.x+box.width);
38962             this.split.el.setTop(box.y);
38963             this.split.el.setHeight(box.height);
38964         }
38965         if(this.collapsed){
38966             this.updateBody(null, box.height);
38967         }
38968         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38969     }
38970 });Roo.namespace("Roo.bootstrap.panel");/*
38971  * Based on:
38972  * Ext JS Library 1.1.1
38973  * Copyright(c) 2006-2007, Ext JS, LLC.
38974  *
38975  * Originally Released Under LGPL - original licence link has changed is not relivant.
38976  *
38977  * Fork - LGPL
38978  * <script type="text/javascript">
38979  */
38980 /**
38981  * @class Roo.ContentPanel
38982  * @extends Roo.util.Observable
38983  * A basic ContentPanel element.
38984  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
38985  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
38986  * @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
38987  * @cfg {Boolean}   closable      True if the panel can be closed/removed
38988  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
38989  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
38990  * @cfg {Toolbar}   toolbar       A toolbar for this panel
38991  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
38992  * @cfg {String} title          The title for this panel
38993  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
38994  * @cfg {String} url            Calls {@link #setUrl} with this value
38995  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
38996  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
38997  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
38998  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
38999  * @cfg {Boolean} badges render the badges
39000
39001  * @constructor
39002  * Create a new ContentPanel.
39003  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39004  * @param {String/Object} config A string to set only the title or a config object
39005  * @param {String} content (optional) Set the HTML content for this panel
39006  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39007  */
39008 Roo.bootstrap.panel.Content = function( config){
39009     
39010     this.tpl = config.tpl || false;
39011     
39012     var el = config.el;
39013     var content = config.content;
39014
39015     if(config.autoCreate){ // xtype is available if this is called from factory
39016         el = Roo.id();
39017     }
39018     this.el = Roo.get(el);
39019     if(!this.el && config && config.autoCreate){
39020         if(typeof config.autoCreate == "object"){
39021             if(!config.autoCreate.id){
39022                 config.autoCreate.id = config.id||el;
39023             }
39024             this.el = Roo.DomHelper.append(document.body,
39025                         config.autoCreate, true);
39026         }else{
39027             var elcfg =  {   tag: "div",
39028                             cls: "roo-layout-inactive-content",
39029                             id: config.id||el
39030                             };
39031             if (config.html) {
39032                 elcfg.html = config.html;
39033                 
39034             }
39035                         
39036             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39037         }
39038     } 
39039     this.closable = false;
39040     this.loaded = false;
39041     this.active = false;
39042    
39043       
39044     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39045         
39046         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39047         
39048         this.wrapEl = this.el; //this.el.wrap();
39049         var ti = [];
39050         if (config.toolbar.items) {
39051             ti = config.toolbar.items ;
39052             delete config.toolbar.items ;
39053         }
39054         
39055         var nitems = [];
39056         this.toolbar.render(this.wrapEl, 'before');
39057         for(var i =0;i < ti.length;i++) {
39058           //  Roo.log(['add child', items[i]]);
39059             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39060         }
39061         this.toolbar.items = nitems;
39062         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39063         delete config.toolbar;
39064         
39065     }
39066     /*
39067     // xtype created footer. - not sure if will work as we normally have to render first..
39068     if (this.footer && !this.footer.el && this.footer.xtype) {
39069         if (!this.wrapEl) {
39070             this.wrapEl = this.el.wrap();
39071         }
39072     
39073         this.footer.container = this.wrapEl.createChild();
39074          
39075         this.footer = Roo.factory(this.footer, Roo);
39076         
39077     }
39078     */
39079     
39080      if(typeof config == "string"){
39081         this.title = config;
39082     }else{
39083         Roo.apply(this, config);
39084     }
39085     
39086     if(this.resizeEl){
39087         this.resizeEl = Roo.get(this.resizeEl, true);
39088     }else{
39089         this.resizeEl = this.el;
39090     }
39091     // handle view.xtype
39092     
39093  
39094     
39095     
39096     this.addEvents({
39097         /**
39098          * @event activate
39099          * Fires when this panel is activated. 
39100          * @param {Roo.ContentPanel} this
39101          */
39102         "activate" : true,
39103         /**
39104          * @event deactivate
39105          * Fires when this panel is activated. 
39106          * @param {Roo.ContentPanel} this
39107          */
39108         "deactivate" : true,
39109
39110         /**
39111          * @event resize
39112          * Fires when this panel is resized if fitToFrame is true.
39113          * @param {Roo.ContentPanel} this
39114          * @param {Number} width The width after any component adjustments
39115          * @param {Number} height The height after any component adjustments
39116          */
39117         "resize" : true,
39118         
39119          /**
39120          * @event render
39121          * Fires when this tab is created
39122          * @param {Roo.ContentPanel} this
39123          */
39124         "render" : true
39125         
39126         
39127         
39128     });
39129     
39130
39131     
39132     
39133     if(this.autoScroll){
39134         this.resizeEl.setStyle("overflow", "auto");
39135     } else {
39136         // fix randome scrolling
39137         //this.el.on('scroll', function() {
39138         //    Roo.log('fix random scolling');
39139         //    this.scrollTo('top',0); 
39140         //});
39141     }
39142     content = content || this.content;
39143     if(content){
39144         this.setContent(content);
39145     }
39146     if(config && config.url){
39147         this.setUrl(this.url, this.params, this.loadOnce);
39148     }
39149     
39150     
39151     
39152     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39153     
39154     if (this.view && typeof(this.view.xtype) != 'undefined') {
39155         this.view.el = this.el.appendChild(document.createElement("div"));
39156         this.view = Roo.factory(this.view); 
39157         this.view.render  &&  this.view.render(false, '');  
39158     }
39159     
39160     
39161     this.fireEvent('render', this);
39162 };
39163
39164 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39165     
39166     tabTip : '',
39167     
39168     setRegion : function(region){
39169         this.region = region;
39170         this.setActiveClass(region && !this.background);
39171     },
39172     
39173     
39174     setActiveClass: function(state)
39175     {
39176         if(state){
39177            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39178            this.el.setStyle('position','relative');
39179         }else{
39180            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39181            this.el.setStyle('position', 'absolute');
39182         } 
39183     },
39184     
39185     /**
39186      * Returns the toolbar for this Panel if one was configured. 
39187      * @return {Roo.Toolbar} 
39188      */
39189     getToolbar : function(){
39190         return this.toolbar;
39191     },
39192     
39193     setActiveState : function(active)
39194     {
39195         this.active = active;
39196         this.setActiveClass(active);
39197         if(!active){
39198             if(this.fireEvent("deactivate", this) === false){
39199                 return false;
39200             }
39201             return true;
39202         }
39203         this.fireEvent("activate", this);
39204         return true;
39205     },
39206     /**
39207      * Updates this panel's element
39208      * @param {String} content The new content
39209      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39210     */
39211     setContent : function(content, loadScripts){
39212         this.el.update(content, loadScripts);
39213     },
39214
39215     ignoreResize : function(w, h){
39216         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39217             return true;
39218         }else{
39219             this.lastSize = {width: w, height: h};
39220             return false;
39221         }
39222     },
39223     /**
39224      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39225      * @return {Roo.UpdateManager} The UpdateManager
39226      */
39227     getUpdateManager : function(){
39228         return this.el.getUpdateManager();
39229     },
39230      /**
39231      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39232      * @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:
39233 <pre><code>
39234 panel.load({
39235     url: "your-url.php",
39236     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39237     callback: yourFunction,
39238     scope: yourObject, //(optional scope)
39239     discardUrl: false,
39240     nocache: false,
39241     text: "Loading...",
39242     timeout: 30,
39243     scripts: false
39244 });
39245 </code></pre>
39246      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39247      * 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.
39248      * @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}
39249      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39250      * @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.
39251      * @return {Roo.ContentPanel} this
39252      */
39253     load : function(){
39254         var um = this.el.getUpdateManager();
39255         um.update.apply(um, arguments);
39256         return this;
39257     },
39258
39259
39260     /**
39261      * 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.
39262      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39263      * @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)
39264      * @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)
39265      * @return {Roo.UpdateManager} The UpdateManager
39266      */
39267     setUrl : function(url, params, loadOnce){
39268         if(this.refreshDelegate){
39269             this.removeListener("activate", this.refreshDelegate);
39270         }
39271         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39272         this.on("activate", this.refreshDelegate);
39273         return this.el.getUpdateManager();
39274     },
39275     
39276     _handleRefresh : function(url, params, loadOnce){
39277         if(!loadOnce || !this.loaded){
39278             var updater = this.el.getUpdateManager();
39279             updater.update(url, params, this._setLoaded.createDelegate(this));
39280         }
39281     },
39282     
39283     _setLoaded : function(){
39284         this.loaded = true;
39285     }, 
39286     
39287     /**
39288      * Returns this panel's id
39289      * @return {String} 
39290      */
39291     getId : function(){
39292         return this.el.id;
39293     },
39294     
39295     /** 
39296      * Returns this panel's element - used by regiosn to add.
39297      * @return {Roo.Element} 
39298      */
39299     getEl : function(){
39300         return this.wrapEl || this.el;
39301     },
39302     
39303    
39304     
39305     adjustForComponents : function(width, height)
39306     {
39307         //Roo.log('adjustForComponents ');
39308         if(this.resizeEl != this.el){
39309             width -= this.el.getFrameWidth('lr');
39310             height -= this.el.getFrameWidth('tb');
39311         }
39312         if(this.toolbar){
39313             var te = this.toolbar.getEl();
39314             te.setWidth(width);
39315             height -= te.getHeight();
39316         }
39317         if(this.footer){
39318             var te = this.footer.getEl();
39319             te.setWidth(width);
39320             height -= te.getHeight();
39321         }
39322         
39323         
39324         if(this.adjustments){
39325             width += this.adjustments[0];
39326             height += this.adjustments[1];
39327         }
39328         return {"width": width, "height": height};
39329     },
39330     
39331     setSize : function(width, height){
39332         if(this.fitToFrame && !this.ignoreResize(width, height)){
39333             if(this.fitContainer && this.resizeEl != this.el){
39334                 this.el.setSize(width, height);
39335             }
39336             var size = this.adjustForComponents(width, height);
39337             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39338             this.fireEvent('resize', this, size.width, size.height);
39339         }
39340     },
39341     
39342     /**
39343      * Returns this panel's title
39344      * @return {String} 
39345      */
39346     getTitle : function(){
39347         
39348         if (typeof(this.title) != 'object') {
39349             return this.title;
39350         }
39351         
39352         var t = '';
39353         for (var k in this.title) {
39354             if (!this.title.hasOwnProperty(k)) {
39355                 continue;
39356             }
39357             
39358             if (k.indexOf('-') >= 0) {
39359                 var s = k.split('-');
39360                 for (var i = 0; i<s.length; i++) {
39361                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39362                 }
39363             } else {
39364                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39365             }
39366         }
39367         return t;
39368     },
39369     
39370     /**
39371      * Set this panel's title
39372      * @param {String} title
39373      */
39374     setTitle : function(title){
39375         this.title = title;
39376         if(this.region){
39377             this.region.updatePanelTitle(this, title);
39378         }
39379     },
39380     
39381     /**
39382      * Returns true is this panel was configured to be closable
39383      * @return {Boolean} 
39384      */
39385     isClosable : function(){
39386         return this.closable;
39387     },
39388     
39389     beforeSlide : function(){
39390         this.el.clip();
39391         this.resizeEl.clip();
39392     },
39393     
39394     afterSlide : function(){
39395         this.el.unclip();
39396         this.resizeEl.unclip();
39397     },
39398     
39399     /**
39400      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39401      *   Will fail silently if the {@link #setUrl} method has not been called.
39402      *   This does not activate the panel, just updates its content.
39403      */
39404     refresh : function(){
39405         if(this.refreshDelegate){
39406            this.loaded = false;
39407            this.refreshDelegate();
39408         }
39409     },
39410     
39411     /**
39412      * Destroys this panel
39413      */
39414     destroy : function(){
39415         this.el.removeAllListeners();
39416         var tempEl = document.createElement("span");
39417         tempEl.appendChild(this.el.dom);
39418         tempEl.innerHTML = "";
39419         this.el.remove();
39420         this.el = null;
39421     },
39422     
39423     /**
39424      * form - if the content panel contains a form - this is a reference to it.
39425      * @type {Roo.form.Form}
39426      */
39427     form : false,
39428     /**
39429      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39430      *    This contains a reference to it.
39431      * @type {Roo.View}
39432      */
39433     view : false,
39434     
39435       /**
39436      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39437      * <pre><code>
39438
39439 layout.addxtype({
39440        xtype : 'Form',
39441        items: [ .... ]
39442    }
39443 );
39444
39445 </code></pre>
39446      * @param {Object} cfg Xtype definition of item to add.
39447      */
39448     
39449     
39450     getChildContainer: function () {
39451         return this.getEl();
39452     }
39453     
39454     
39455     /*
39456         var  ret = new Roo.factory(cfg);
39457         return ret;
39458         
39459         
39460         // add form..
39461         if (cfg.xtype.match(/^Form$/)) {
39462             
39463             var el;
39464             //if (this.footer) {
39465             //    el = this.footer.container.insertSibling(false, 'before');
39466             //} else {
39467                 el = this.el.createChild();
39468             //}
39469
39470             this.form = new  Roo.form.Form(cfg);
39471             
39472             
39473             if ( this.form.allItems.length) {
39474                 this.form.render(el.dom);
39475             }
39476             return this.form;
39477         }
39478         // should only have one of theses..
39479         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39480             // views.. should not be just added - used named prop 'view''
39481             
39482             cfg.el = this.el.appendChild(document.createElement("div"));
39483             // factory?
39484             
39485             var ret = new Roo.factory(cfg);
39486              
39487              ret.render && ret.render(false, ''); // render blank..
39488             this.view = ret;
39489             return ret;
39490         }
39491         return false;
39492     }
39493     \*/
39494 });
39495  
39496 /**
39497  * @class Roo.bootstrap.panel.Grid
39498  * @extends Roo.bootstrap.panel.Content
39499  * @constructor
39500  * Create a new GridPanel.
39501  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39502  * @param {Object} config A the config object
39503   
39504  */
39505
39506
39507
39508 Roo.bootstrap.panel.Grid = function(config)
39509 {
39510     
39511       
39512     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39513         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39514
39515     config.el = this.wrapper;
39516     //this.el = this.wrapper;
39517     
39518       if (config.container) {
39519         // ctor'ed from a Border/panel.grid
39520         
39521         
39522         this.wrapper.setStyle("overflow", "hidden");
39523         this.wrapper.addClass('roo-grid-container');
39524
39525     }
39526     
39527     
39528     if(config.toolbar){
39529         var tool_el = this.wrapper.createChild();    
39530         this.toolbar = Roo.factory(config.toolbar);
39531         var ti = [];
39532         if (config.toolbar.items) {
39533             ti = config.toolbar.items ;
39534             delete config.toolbar.items ;
39535         }
39536         
39537         var nitems = [];
39538         this.toolbar.render(tool_el);
39539         for(var i =0;i < ti.length;i++) {
39540           //  Roo.log(['add child', items[i]]);
39541             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39542         }
39543         this.toolbar.items = nitems;
39544         
39545         delete config.toolbar;
39546     }
39547     
39548     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39549     config.grid.scrollBody = true;;
39550     config.grid.monitorWindowResize = false; // turn off autosizing
39551     config.grid.autoHeight = false;
39552     config.grid.autoWidth = false;
39553     
39554     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39555     
39556     if (config.background) {
39557         // render grid on panel activation (if panel background)
39558         this.on('activate', function(gp) {
39559             if (!gp.grid.rendered) {
39560                 gp.grid.render(this.wrapper);
39561                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39562             }
39563         });
39564             
39565     } else {
39566         this.grid.render(this.wrapper);
39567         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39568
39569     }
39570     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39571     // ??? needed ??? config.el = this.wrapper;
39572     
39573     
39574     
39575   
39576     // xtype created footer. - not sure if will work as we normally have to render first..
39577     if (this.footer && !this.footer.el && this.footer.xtype) {
39578         
39579         var ctr = this.grid.getView().getFooterPanel(true);
39580         this.footer.dataSource = this.grid.dataSource;
39581         this.footer = Roo.factory(this.footer, Roo);
39582         this.footer.render(ctr);
39583         
39584     }
39585     
39586     
39587     
39588     
39589      
39590 };
39591
39592 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39593     getId : function(){
39594         return this.grid.id;
39595     },
39596     
39597     /**
39598      * Returns the grid for this panel
39599      * @return {Roo.bootstrap.Table} 
39600      */
39601     getGrid : function(){
39602         return this.grid;    
39603     },
39604     
39605     setSize : function(width, height){
39606         if(!this.ignoreResize(width, height)){
39607             var grid = this.grid;
39608             var size = this.adjustForComponents(width, height);
39609             // tfoot is not a footer?
39610           
39611             
39612             var gridel = grid.getGridEl();
39613             gridel.setSize(size.width, size.height);
39614             
39615             var tbd = grid.getGridEl().select('tbody', true).first();
39616             var thd = grid.getGridEl().select('thead',true).first();
39617             var tbf= grid.getGridEl().select('tfoot', true).first();
39618
39619             if (tbf) {
39620                 size.height -= thd.getHeight();
39621             }
39622             if (thd) {
39623                 size.height -= thd.getHeight();
39624             }
39625             
39626             tbd.setSize(size.width, size.height );
39627             // this is for the account management tab -seems to work there.
39628             var thd = grid.getGridEl().select('thead',true).first();
39629             //if (tbd) {
39630             //    tbd.setSize(size.width, size.height - thd.getHeight());
39631             //}
39632              
39633             grid.autoSize();
39634         }
39635     },
39636      
39637     
39638     
39639     beforeSlide : function(){
39640         this.grid.getView().scroller.clip();
39641     },
39642     
39643     afterSlide : function(){
39644         this.grid.getView().scroller.unclip();
39645     },
39646     
39647     destroy : function(){
39648         this.grid.destroy();
39649         delete this.grid;
39650         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39651     }
39652 });
39653
39654 /**
39655  * @class Roo.bootstrap.panel.Nest
39656  * @extends Roo.bootstrap.panel.Content
39657  * @constructor
39658  * Create a new Panel, that can contain a layout.Border.
39659  * 
39660  * 
39661  * @param {Roo.BorderLayout} layout The layout for this panel
39662  * @param {String/Object} config A string to set only the title or a config object
39663  */
39664 Roo.bootstrap.panel.Nest = function(config)
39665 {
39666     // construct with only one argument..
39667     /* FIXME - implement nicer consturctors
39668     if (layout.layout) {
39669         config = layout;
39670         layout = config.layout;
39671         delete config.layout;
39672     }
39673     if (layout.xtype && !layout.getEl) {
39674         // then layout needs constructing..
39675         layout = Roo.factory(layout, Roo);
39676     }
39677     */
39678     
39679     config.el =  config.layout.getEl();
39680     
39681     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39682     
39683     config.layout.monitorWindowResize = false; // turn off autosizing
39684     this.layout = config.layout;
39685     this.layout.getEl().addClass("roo-layout-nested-layout");
39686     this.layout.parent = this;
39687     
39688     
39689     
39690     
39691 };
39692
39693 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39694
39695     setSize : function(width, height){
39696         if(!this.ignoreResize(width, height)){
39697             var size = this.adjustForComponents(width, height);
39698             var el = this.layout.getEl();
39699             if (size.height < 1) {
39700                 el.setWidth(size.width);   
39701             } else {
39702                 el.setSize(size.width, size.height);
39703             }
39704             var touch = el.dom.offsetWidth;
39705             this.layout.layout();
39706             // ie requires a double layout on the first pass
39707             if(Roo.isIE && !this.initialized){
39708                 this.initialized = true;
39709                 this.layout.layout();
39710             }
39711         }
39712     },
39713     
39714     // activate all subpanels if not currently active..
39715     
39716     setActiveState : function(active){
39717         this.active = active;
39718         this.setActiveClass(active);
39719         
39720         if(!active){
39721             this.fireEvent("deactivate", this);
39722             return;
39723         }
39724         
39725         this.fireEvent("activate", this);
39726         // not sure if this should happen before or after..
39727         if (!this.layout) {
39728             return; // should not happen..
39729         }
39730         var reg = false;
39731         for (var r in this.layout.regions) {
39732             reg = this.layout.getRegion(r);
39733             if (reg.getActivePanel()) {
39734                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39735                 reg.setActivePanel(reg.getActivePanel());
39736                 continue;
39737             }
39738             if (!reg.panels.length) {
39739                 continue;
39740             }
39741             reg.showPanel(reg.getPanel(0));
39742         }
39743         
39744         
39745         
39746         
39747     },
39748     
39749     /**
39750      * Returns the nested BorderLayout for this panel
39751      * @return {Roo.BorderLayout} 
39752      */
39753     getLayout : function(){
39754         return this.layout;
39755     },
39756     
39757      /**
39758      * Adds a xtype elements to the layout of the nested panel
39759      * <pre><code>
39760
39761 panel.addxtype({
39762        xtype : 'ContentPanel',
39763        region: 'west',
39764        items: [ .... ]
39765    }
39766 );
39767
39768 panel.addxtype({
39769         xtype : 'NestedLayoutPanel',
39770         region: 'west',
39771         layout: {
39772            center: { },
39773            west: { }   
39774         },
39775         items : [ ... list of content panels or nested layout panels.. ]
39776    }
39777 );
39778 </code></pre>
39779      * @param {Object} cfg Xtype definition of item to add.
39780      */
39781     addxtype : function(cfg) {
39782         return this.layout.addxtype(cfg);
39783     
39784     }
39785 });/*
39786  * Based on:
39787  * Ext JS Library 1.1.1
39788  * Copyright(c) 2006-2007, Ext JS, LLC.
39789  *
39790  * Originally Released Under LGPL - original licence link has changed is not relivant.
39791  *
39792  * Fork - LGPL
39793  * <script type="text/javascript">
39794  */
39795 /**
39796  * @class Roo.TabPanel
39797  * @extends Roo.util.Observable
39798  * A lightweight tab container.
39799  * <br><br>
39800  * Usage:
39801  * <pre><code>
39802 // basic tabs 1, built from existing content
39803 var tabs = new Roo.TabPanel("tabs1");
39804 tabs.addTab("script", "View Script");
39805 tabs.addTab("markup", "View Markup");
39806 tabs.activate("script");
39807
39808 // more advanced tabs, built from javascript
39809 var jtabs = new Roo.TabPanel("jtabs");
39810 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39811
39812 // set up the UpdateManager
39813 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39814 var updater = tab2.getUpdateManager();
39815 updater.setDefaultUrl("ajax1.htm");
39816 tab2.on('activate', updater.refresh, updater, true);
39817
39818 // Use setUrl for Ajax loading
39819 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39820 tab3.setUrl("ajax2.htm", null, true);
39821
39822 // Disabled tab
39823 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39824 tab4.disable();
39825
39826 jtabs.activate("jtabs-1");
39827  * </code></pre>
39828  * @constructor
39829  * Create a new TabPanel.
39830  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39831  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39832  */
39833 Roo.bootstrap.panel.Tabs = function(config){
39834     /**
39835     * The container element for this TabPanel.
39836     * @type Roo.Element
39837     */
39838     this.el = Roo.get(config.el);
39839     delete config.el;
39840     if(config){
39841         if(typeof config == "boolean"){
39842             this.tabPosition = config ? "bottom" : "top";
39843         }else{
39844             Roo.apply(this, config);
39845         }
39846     }
39847     
39848     if(this.tabPosition == "bottom"){
39849         // if tabs are at the bottom = create the body first.
39850         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39851         this.el.addClass("roo-tabs-bottom");
39852     }
39853     // next create the tabs holders
39854     
39855     if (this.tabPosition == "west"){
39856         
39857         var reg = this.region; // fake it..
39858         while (reg) {
39859             if (!reg.mgr.parent) {
39860                 break;
39861             }
39862             reg = reg.mgr.parent.region;
39863         }
39864         Roo.log("got nest?");
39865         Roo.log(reg);
39866         if (reg.mgr.getRegion('west')) {
39867             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
39868             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
39869             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39870             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39871             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39872         
39873             
39874         }
39875         
39876         
39877     } else {
39878      
39879         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
39880         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
39881         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
39882         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
39883     }
39884     
39885     
39886     if(Roo.isIE){
39887         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
39888     }
39889     
39890     // finally - if tabs are at the top, then create the body last..
39891     if(this.tabPosition != "bottom"){
39892         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
39893          * @type Roo.Element
39894          */
39895         this.bodyEl = Roo.get(this.createBody(this.el.dom));
39896         this.el.addClass("roo-tabs-top");
39897     }
39898     this.items = [];
39899
39900     this.bodyEl.setStyle("position", "relative");
39901
39902     this.active = null;
39903     this.activateDelegate = this.activate.createDelegate(this);
39904
39905     this.addEvents({
39906         /**
39907          * @event tabchange
39908          * Fires when the active tab changes
39909          * @param {Roo.TabPanel} this
39910          * @param {Roo.TabPanelItem} activePanel The new active tab
39911          */
39912         "tabchange": true,
39913         /**
39914          * @event beforetabchange
39915          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
39916          * @param {Roo.TabPanel} this
39917          * @param {Object} e Set cancel to true on this object to cancel the tab change
39918          * @param {Roo.TabPanelItem} tab The tab being changed to
39919          */
39920         "beforetabchange" : true
39921     });
39922
39923     Roo.EventManager.onWindowResize(this.onResize, this);
39924     this.cpad = this.el.getPadding("lr");
39925     this.hiddenCount = 0;
39926
39927
39928     // toolbar on the tabbar support...
39929     if (this.toolbar) {
39930         alert("no toolbar support yet");
39931         this.toolbar  = false;
39932         /*
39933         var tcfg = this.toolbar;
39934         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
39935         this.toolbar = new Roo.Toolbar(tcfg);
39936         if (Roo.isSafari) {
39937             var tbl = tcfg.container.child('table', true);
39938             tbl.setAttribute('width', '100%');
39939         }
39940         */
39941         
39942     }
39943    
39944
39945
39946     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
39947 };
39948
39949 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
39950     /*
39951      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
39952      */
39953     tabPosition : "top",
39954     /*
39955      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
39956      */
39957     currentTabWidth : 0,
39958     /*
39959      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
39960      */
39961     minTabWidth : 40,
39962     /*
39963      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
39964      */
39965     maxTabWidth : 250,
39966     /*
39967      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
39968      */
39969     preferredTabWidth : 175,
39970     /*
39971      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
39972      */
39973     resizeTabs : false,
39974     /*
39975      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
39976      */
39977     monitorResize : true,
39978     /*
39979      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
39980      */
39981     toolbar : false,  // set by caller..
39982     
39983     region : false, /// set by caller
39984     
39985     disableTooltips : true, // not used yet...
39986
39987     /**
39988      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
39989      * @param {String} id The id of the div to use <b>or create</b>
39990      * @param {String} text The text for the tab
39991      * @param {String} content (optional) Content to put in the TabPanelItem body
39992      * @param {Boolean} closable (optional) True to create a close icon on the tab
39993      * @return {Roo.TabPanelItem} The created TabPanelItem
39994      */
39995     addTab : function(id, text, content, closable, tpl)
39996     {
39997         var item = new Roo.bootstrap.panel.TabItem({
39998             panel: this,
39999             id : id,
40000             text : text,
40001             closable : closable,
40002             tpl : tpl
40003         });
40004         this.addTabItem(item);
40005         if(content){
40006             item.setContent(content);
40007         }
40008         return item;
40009     },
40010
40011     /**
40012      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40013      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40014      * @return {Roo.TabPanelItem}
40015      */
40016     getTab : function(id){
40017         return this.items[id];
40018     },
40019
40020     /**
40021      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40022      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40023      */
40024     hideTab : function(id){
40025         var t = this.items[id];
40026         if(!t.isHidden()){
40027            t.setHidden(true);
40028            this.hiddenCount++;
40029            this.autoSizeTabs();
40030         }
40031     },
40032
40033     /**
40034      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40035      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40036      */
40037     unhideTab : function(id){
40038         var t = this.items[id];
40039         if(t.isHidden()){
40040            t.setHidden(false);
40041            this.hiddenCount--;
40042            this.autoSizeTabs();
40043         }
40044     },
40045
40046     /**
40047      * Adds an existing {@link Roo.TabPanelItem}.
40048      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40049      */
40050     addTabItem : function(item)
40051     {
40052         this.items[item.id] = item;
40053         this.items.push(item);
40054         this.autoSizeTabs();
40055       //  if(this.resizeTabs){
40056     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40057   //         this.autoSizeTabs();
40058 //        }else{
40059 //            item.autoSize();
40060        // }
40061     },
40062
40063     /**
40064      * Removes a {@link Roo.TabPanelItem}.
40065      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40066      */
40067     removeTab : function(id){
40068         var items = this.items;
40069         var tab = items[id];
40070         if(!tab) { return; }
40071         var index = items.indexOf(tab);
40072         if(this.active == tab && items.length > 1){
40073             var newTab = this.getNextAvailable(index);
40074             if(newTab) {
40075                 newTab.activate();
40076             }
40077         }
40078         this.stripEl.dom.removeChild(tab.pnode.dom);
40079         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40080             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40081         }
40082         items.splice(index, 1);
40083         delete this.items[tab.id];
40084         tab.fireEvent("close", tab);
40085         tab.purgeListeners();
40086         this.autoSizeTabs();
40087     },
40088
40089     getNextAvailable : function(start){
40090         var items = this.items;
40091         var index = start;
40092         // look for a next tab that will slide over to
40093         // replace the one being removed
40094         while(index < items.length){
40095             var item = items[++index];
40096             if(item && !item.isHidden()){
40097                 return item;
40098             }
40099         }
40100         // if one isn't found select the previous tab (on the left)
40101         index = start;
40102         while(index >= 0){
40103             var item = items[--index];
40104             if(item && !item.isHidden()){
40105                 return item;
40106             }
40107         }
40108         return null;
40109     },
40110
40111     /**
40112      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40113      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40114      */
40115     disableTab : function(id){
40116         var tab = this.items[id];
40117         if(tab && this.active != tab){
40118             tab.disable();
40119         }
40120     },
40121
40122     /**
40123      * Enables a {@link Roo.TabPanelItem} that is disabled.
40124      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40125      */
40126     enableTab : function(id){
40127         var tab = this.items[id];
40128         tab.enable();
40129     },
40130
40131     /**
40132      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40133      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40134      * @return {Roo.TabPanelItem} The TabPanelItem.
40135      */
40136     activate : function(id)
40137     {
40138         //Roo.log('activite:'  + id);
40139         
40140         var tab = this.items[id];
40141         if(!tab){
40142             return null;
40143         }
40144         if(tab == this.active || tab.disabled){
40145             return tab;
40146         }
40147         var e = {};
40148         this.fireEvent("beforetabchange", this, e, tab);
40149         if(e.cancel !== true && !tab.disabled){
40150             if(this.active){
40151                 this.active.hide();
40152             }
40153             this.active = this.items[id];
40154             this.active.show();
40155             this.fireEvent("tabchange", this, this.active);
40156         }
40157         return tab;
40158     },
40159
40160     /**
40161      * Gets the active {@link Roo.TabPanelItem}.
40162      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40163      */
40164     getActiveTab : function(){
40165         return this.active;
40166     },
40167
40168     /**
40169      * Updates the tab body element to fit the height of the container element
40170      * for overflow scrolling
40171      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40172      */
40173     syncHeight : function(targetHeight){
40174         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40175         var bm = this.bodyEl.getMargins();
40176         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40177         this.bodyEl.setHeight(newHeight);
40178         return newHeight;
40179     },
40180
40181     onResize : function(){
40182         if(this.monitorResize){
40183             this.autoSizeTabs();
40184         }
40185     },
40186
40187     /**
40188      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40189      */
40190     beginUpdate : function(){
40191         this.updating = true;
40192     },
40193
40194     /**
40195      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40196      */
40197     endUpdate : function(){
40198         this.updating = false;
40199         this.autoSizeTabs();
40200     },
40201
40202     /**
40203      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40204      */
40205     autoSizeTabs : function()
40206     {
40207         var count = this.items.length;
40208         var vcount = count - this.hiddenCount;
40209         
40210         if (vcount < 2) {
40211             this.stripEl.hide();
40212         } else {
40213             this.stripEl.show();
40214         }
40215         
40216         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40217             return;
40218         }
40219         
40220         
40221         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40222         var availWidth = Math.floor(w / vcount);
40223         var b = this.stripBody;
40224         if(b.getWidth() > w){
40225             var tabs = this.items;
40226             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40227             if(availWidth < this.minTabWidth){
40228                 /*if(!this.sleft){    // incomplete scrolling code
40229                     this.createScrollButtons();
40230                 }
40231                 this.showScroll();
40232                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40233             }
40234         }else{
40235             if(this.currentTabWidth < this.preferredTabWidth){
40236                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40237             }
40238         }
40239     },
40240
40241     /**
40242      * Returns the number of tabs in this TabPanel.
40243      * @return {Number}
40244      */
40245      getCount : function(){
40246          return this.items.length;
40247      },
40248
40249     /**
40250      * Resizes all the tabs to the passed width
40251      * @param {Number} The new width
40252      */
40253     setTabWidth : function(width){
40254         this.currentTabWidth = width;
40255         for(var i = 0, len = this.items.length; i < len; i++) {
40256                 if(!this.items[i].isHidden()) {
40257                 this.items[i].setWidth(width);
40258             }
40259         }
40260     },
40261
40262     /**
40263      * Destroys this TabPanel
40264      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40265      */
40266     destroy : function(removeEl){
40267         Roo.EventManager.removeResizeListener(this.onResize, this);
40268         for(var i = 0, len = this.items.length; i < len; i++){
40269             this.items[i].purgeListeners();
40270         }
40271         if(removeEl === true){
40272             this.el.update("");
40273             this.el.remove();
40274         }
40275     },
40276     
40277     createStrip : function(container)
40278     {
40279         var strip = document.createElement("nav");
40280         strip.className = Roo.bootstrap.version == 4 ?
40281             "navbar-light bg-light" : 
40282             "navbar navbar-default"; //"x-tabs-wrap";
40283         container.appendChild(strip);
40284         return strip;
40285     },
40286     
40287     createStripList : function(strip)
40288     {
40289         // div wrapper for retard IE
40290         // returns the "tr" element.
40291         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40292         //'<div class="x-tabs-strip-wrap">'+
40293           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40294           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40295         return strip.firstChild; //.firstChild.firstChild.firstChild;
40296     },
40297     createBody : function(container)
40298     {
40299         var body = document.createElement("div");
40300         Roo.id(body, "tab-body");
40301         //Roo.fly(body).addClass("x-tabs-body");
40302         Roo.fly(body).addClass("tab-content");
40303         container.appendChild(body);
40304         return body;
40305     },
40306     createItemBody :function(bodyEl, id){
40307         var body = Roo.getDom(id);
40308         if(!body){
40309             body = document.createElement("div");
40310             body.id = id;
40311         }
40312         //Roo.fly(body).addClass("x-tabs-item-body");
40313         Roo.fly(body).addClass("tab-pane");
40314          bodyEl.insertBefore(body, bodyEl.firstChild);
40315         return body;
40316     },
40317     /** @private */
40318     createStripElements :  function(stripEl, text, closable, tpl)
40319     {
40320         var td = document.createElement("li"); // was td..
40321         td.className = 'nav-item';
40322         
40323         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40324         
40325         
40326         stripEl.appendChild(td);
40327         /*if(closable){
40328             td.className = "x-tabs-closable";
40329             if(!this.closeTpl){
40330                 this.closeTpl = new Roo.Template(
40331                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40332                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40333                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40334                 );
40335             }
40336             var el = this.closeTpl.overwrite(td, {"text": text});
40337             var close = el.getElementsByTagName("div")[0];
40338             var inner = el.getElementsByTagName("em")[0];
40339             return {"el": el, "close": close, "inner": inner};
40340         } else {
40341         */
40342         // not sure what this is..
40343 //            if(!this.tabTpl){
40344                 //this.tabTpl = new Roo.Template(
40345                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40346                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40347                 //);
40348 //                this.tabTpl = new Roo.Template(
40349 //                   '<a href="#">' +
40350 //                   '<span unselectable="on"' +
40351 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40352 //                            ' >{text}</span></a>'
40353 //                );
40354 //                
40355 //            }
40356
40357
40358             var template = tpl || this.tabTpl || false;
40359             
40360             if(!template){
40361                 template =  new Roo.Template(
40362                         Roo.bootstrap.version == 4 ? 
40363                             (
40364                                 '<a class="nav-link" href="#" unselectable="on"' +
40365                                      (this.disableTooltips ? '' : ' title="{text}"') +
40366                                      ' >{text}</a>'
40367                             ) : (
40368                                 '<a class="nav-link" href="#">' +
40369                                 '<span unselectable="on"' +
40370                                          (this.disableTooltips ? '' : ' title="{text}"') +
40371                                     ' >{text}</span></a>'
40372                             )
40373                 );
40374             }
40375             
40376             switch (typeof(template)) {
40377                 case 'object' :
40378                     break;
40379                 case 'string' :
40380                     template = new Roo.Template(template);
40381                     break;
40382                 default :
40383                     break;
40384             }
40385             
40386             var el = template.overwrite(td, {"text": text});
40387             
40388             var inner = el.getElementsByTagName("span")[0];
40389             
40390             return {"el": el, "inner": inner};
40391             
40392     }
40393         
40394     
40395 });
40396
40397 /**
40398  * @class Roo.TabPanelItem
40399  * @extends Roo.util.Observable
40400  * Represents an individual item (tab plus body) in a TabPanel.
40401  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40402  * @param {String} id The id of this TabPanelItem
40403  * @param {String} text The text for the tab of this TabPanelItem
40404  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40405  */
40406 Roo.bootstrap.panel.TabItem = function(config){
40407     /**
40408      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40409      * @type Roo.TabPanel
40410      */
40411     this.tabPanel = config.panel;
40412     /**
40413      * The id for this TabPanelItem
40414      * @type String
40415      */
40416     this.id = config.id;
40417     /** @private */
40418     this.disabled = false;
40419     /** @private */
40420     this.text = config.text;
40421     /** @private */
40422     this.loaded = false;
40423     this.closable = config.closable;
40424
40425     /**
40426      * The body element for this TabPanelItem.
40427      * @type Roo.Element
40428      */
40429     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40430     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40431     this.bodyEl.setStyle("display", "block");
40432     this.bodyEl.setStyle("zoom", "1");
40433     //this.hideAction();
40434
40435     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40436     /** @private */
40437     this.el = Roo.get(els.el);
40438     this.inner = Roo.get(els.inner, true);
40439      this.textEl = Roo.bootstrap.version == 4 ?
40440         this.el : Roo.get(this.el.dom.firstChild, true);
40441
40442     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40443     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40444
40445     
40446 //    this.el.on("mousedown", this.onTabMouseDown, this);
40447     this.el.on("click", this.onTabClick, this);
40448     /** @private */
40449     if(config.closable){
40450         var c = Roo.get(els.close, true);
40451         c.dom.title = this.closeText;
40452         c.addClassOnOver("close-over");
40453         c.on("click", this.closeClick, this);
40454      }
40455
40456     this.addEvents({
40457          /**
40458          * @event activate
40459          * Fires when this tab becomes the active tab.
40460          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40461          * @param {Roo.TabPanelItem} this
40462          */
40463         "activate": true,
40464         /**
40465          * @event beforeclose
40466          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40467          * @param {Roo.TabPanelItem} this
40468          * @param {Object} e Set cancel to true on this object to cancel the close.
40469          */
40470         "beforeclose": true,
40471         /**
40472          * @event close
40473          * Fires when this tab is closed.
40474          * @param {Roo.TabPanelItem} this
40475          */
40476          "close": true,
40477         /**
40478          * @event deactivate
40479          * Fires when this tab is no longer the active tab.
40480          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40481          * @param {Roo.TabPanelItem} this
40482          */
40483          "deactivate" : true
40484     });
40485     this.hidden = false;
40486
40487     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40488 };
40489
40490 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40491            {
40492     purgeListeners : function(){
40493        Roo.util.Observable.prototype.purgeListeners.call(this);
40494        this.el.removeAllListeners();
40495     },
40496     /**
40497      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40498      */
40499     show : function(){
40500         this.status_node.addClass("active");
40501         this.showAction();
40502         if(Roo.isOpera){
40503             this.tabPanel.stripWrap.repaint();
40504         }
40505         this.fireEvent("activate", this.tabPanel, this);
40506     },
40507
40508     /**
40509      * Returns true if this tab is the active tab.
40510      * @return {Boolean}
40511      */
40512     isActive : function(){
40513         return this.tabPanel.getActiveTab() == this;
40514     },
40515
40516     /**
40517      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40518      */
40519     hide : function(){
40520         this.status_node.removeClass("active");
40521         this.hideAction();
40522         this.fireEvent("deactivate", this.tabPanel, this);
40523     },
40524
40525     hideAction : function(){
40526         this.bodyEl.hide();
40527         this.bodyEl.setStyle("position", "absolute");
40528         this.bodyEl.setLeft("-20000px");
40529         this.bodyEl.setTop("-20000px");
40530     },
40531
40532     showAction : function(){
40533         this.bodyEl.setStyle("position", "relative");
40534         this.bodyEl.setTop("");
40535         this.bodyEl.setLeft("");
40536         this.bodyEl.show();
40537     },
40538
40539     /**
40540      * Set the tooltip for the tab.
40541      * @param {String} tooltip The tab's tooltip
40542      */
40543     setTooltip : function(text){
40544         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40545             this.textEl.dom.qtip = text;
40546             this.textEl.dom.removeAttribute('title');
40547         }else{
40548             this.textEl.dom.title = text;
40549         }
40550     },
40551
40552     onTabClick : function(e){
40553         e.preventDefault();
40554         this.tabPanel.activate(this.id);
40555     },
40556
40557     onTabMouseDown : function(e){
40558         e.preventDefault();
40559         this.tabPanel.activate(this.id);
40560     },
40561 /*
40562     getWidth : function(){
40563         return this.inner.getWidth();
40564     },
40565
40566     setWidth : function(width){
40567         var iwidth = width - this.linode.getPadding("lr");
40568         this.inner.setWidth(iwidth);
40569         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40570         this.linode.setWidth(width);
40571     },
40572 */
40573     /**
40574      * Show or hide the tab
40575      * @param {Boolean} hidden True to hide or false to show.
40576      */
40577     setHidden : function(hidden){
40578         this.hidden = hidden;
40579         this.linode.setStyle("display", hidden ? "none" : "");
40580     },
40581
40582     /**
40583      * Returns true if this tab is "hidden"
40584      * @return {Boolean}
40585      */
40586     isHidden : function(){
40587         return this.hidden;
40588     },
40589
40590     /**
40591      * Returns the text for this tab
40592      * @return {String}
40593      */
40594     getText : function(){
40595         return this.text;
40596     },
40597     /*
40598     autoSize : function(){
40599         //this.el.beginMeasure();
40600         this.textEl.setWidth(1);
40601         /*
40602          *  #2804 [new] Tabs in Roojs
40603          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40604          */
40605         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40606         //this.el.endMeasure();
40607     //},
40608
40609     /**
40610      * Sets the text for the tab (Note: this also sets the tooltip text)
40611      * @param {String} text The tab's text and tooltip
40612      */
40613     setText : function(text){
40614         this.text = text;
40615         this.textEl.update(text);
40616         this.setTooltip(text);
40617         //if(!this.tabPanel.resizeTabs){
40618         //    this.autoSize();
40619         //}
40620     },
40621     /**
40622      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40623      */
40624     activate : function(){
40625         this.tabPanel.activate(this.id);
40626     },
40627
40628     /**
40629      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40630      */
40631     disable : function(){
40632         if(this.tabPanel.active != this){
40633             this.disabled = true;
40634             this.status_node.addClass("disabled");
40635         }
40636     },
40637
40638     /**
40639      * Enables this TabPanelItem if it was previously disabled.
40640      */
40641     enable : function(){
40642         this.disabled = false;
40643         this.status_node.removeClass("disabled");
40644     },
40645
40646     /**
40647      * Sets the content for this TabPanelItem.
40648      * @param {String} content The content
40649      * @param {Boolean} loadScripts true to look for and load scripts
40650      */
40651     setContent : function(content, loadScripts){
40652         this.bodyEl.update(content, loadScripts);
40653     },
40654
40655     /**
40656      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40657      * @return {Roo.UpdateManager} The UpdateManager
40658      */
40659     getUpdateManager : function(){
40660         return this.bodyEl.getUpdateManager();
40661     },
40662
40663     /**
40664      * Set a URL to be used to load the content for this TabPanelItem.
40665      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40666      * @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)
40667      * @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)
40668      * @return {Roo.UpdateManager} The UpdateManager
40669      */
40670     setUrl : function(url, params, loadOnce){
40671         if(this.refreshDelegate){
40672             this.un('activate', this.refreshDelegate);
40673         }
40674         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40675         this.on("activate", this.refreshDelegate);
40676         return this.bodyEl.getUpdateManager();
40677     },
40678
40679     /** @private */
40680     _handleRefresh : function(url, params, loadOnce){
40681         if(!loadOnce || !this.loaded){
40682             var updater = this.bodyEl.getUpdateManager();
40683             updater.update(url, params, this._setLoaded.createDelegate(this));
40684         }
40685     },
40686
40687     /**
40688      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40689      *   Will fail silently if the setUrl method has not been called.
40690      *   This does not activate the panel, just updates its content.
40691      */
40692     refresh : function(){
40693         if(this.refreshDelegate){
40694            this.loaded = false;
40695            this.refreshDelegate();
40696         }
40697     },
40698
40699     /** @private */
40700     _setLoaded : function(){
40701         this.loaded = true;
40702     },
40703
40704     /** @private */
40705     closeClick : function(e){
40706         var o = {};
40707         e.stopEvent();
40708         this.fireEvent("beforeclose", this, o);
40709         if(o.cancel !== true){
40710             this.tabPanel.removeTab(this.id);
40711         }
40712     },
40713     /**
40714      * The text displayed in the tooltip for the close icon.
40715      * @type String
40716      */
40717     closeText : "Close this tab"
40718 });
40719 /**
40720 *    This script refer to:
40721 *    Title: International Telephone Input
40722 *    Author: Jack O'Connor
40723 *    Code version:  v12.1.12
40724 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40725 **/
40726
40727 Roo.bootstrap.PhoneInputData = function() {
40728     var d = [
40729       [
40730         "Afghanistan (‫افغانستان‬‎)",
40731         "af",
40732         "93"
40733       ],
40734       [
40735         "Albania (Shqipëri)",
40736         "al",
40737         "355"
40738       ],
40739       [
40740         "Algeria (‫الجزائر‬‎)",
40741         "dz",
40742         "213"
40743       ],
40744       [
40745         "American Samoa",
40746         "as",
40747         "1684"
40748       ],
40749       [
40750         "Andorra",
40751         "ad",
40752         "376"
40753       ],
40754       [
40755         "Angola",
40756         "ao",
40757         "244"
40758       ],
40759       [
40760         "Anguilla",
40761         "ai",
40762         "1264"
40763       ],
40764       [
40765         "Antigua and Barbuda",
40766         "ag",
40767         "1268"
40768       ],
40769       [
40770         "Argentina",
40771         "ar",
40772         "54"
40773       ],
40774       [
40775         "Armenia (Հայաստան)",
40776         "am",
40777         "374"
40778       ],
40779       [
40780         "Aruba",
40781         "aw",
40782         "297"
40783       ],
40784       [
40785         "Australia",
40786         "au",
40787         "61",
40788         0
40789       ],
40790       [
40791         "Austria (Österreich)",
40792         "at",
40793         "43"
40794       ],
40795       [
40796         "Azerbaijan (Azərbaycan)",
40797         "az",
40798         "994"
40799       ],
40800       [
40801         "Bahamas",
40802         "bs",
40803         "1242"
40804       ],
40805       [
40806         "Bahrain (‫البحرين‬‎)",
40807         "bh",
40808         "973"
40809       ],
40810       [
40811         "Bangladesh (বাংলাদেশ)",
40812         "bd",
40813         "880"
40814       ],
40815       [
40816         "Barbados",
40817         "bb",
40818         "1246"
40819       ],
40820       [
40821         "Belarus (Беларусь)",
40822         "by",
40823         "375"
40824       ],
40825       [
40826         "Belgium (België)",
40827         "be",
40828         "32"
40829       ],
40830       [
40831         "Belize",
40832         "bz",
40833         "501"
40834       ],
40835       [
40836         "Benin (Bénin)",
40837         "bj",
40838         "229"
40839       ],
40840       [
40841         "Bermuda",
40842         "bm",
40843         "1441"
40844       ],
40845       [
40846         "Bhutan (འབྲུག)",
40847         "bt",
40848         "975"
40849       ],
40850       [
40851         "Bolivia",
40852         "bo",
40853         "591"
40854       ],
40855       [
40856         "Bosnia and Herzegovina (Босна и Херцеговина)",
40857         "ba",
40858         "387"
40859       ],
40860       [
40861         "Botswana",
40862         "bw",
40863         "267"
40864       ],
40865       [
40866         "Brazil (Brasil)",
40867         "br",
40868         "55"
40869       ],
40870       [
40871         "British Indian Ocean Territory",
40872         "io",
40873         "246"
40874       ],
40875       [
40876         "British Virgin Islands",
40877         "vg",
40878         "1284"
40879       ],
40880       [
40881         "Brunei",
40882         "bn",
40883         "673"
40884       ],
40885       [
40886         "Bulgaria (България)",
40887         "bg",
40888         "359"
40889       ],
40890       [
40891         "Burkina Faso",
40892         "bf",
40893         "226"
40894       ],
40895       [
40896         "Burundi (Uburundi)",
40897         "bi",
40898         "257"
40899       ],
40900       [
40901         "Cambodia (កម្ពុជា)",
40902         "kh",
40903         "855"
40904       ],
40905       [
40906         "Cameroon (Cameroun)",
40907         "cm",
40908         "237"
40909       ],
40910       [
40911         "Canada",
40912         "ca",
40913         "1",
40914         1,
40915         ["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"]
40916       ],
40917       [
40918         "Cape Verde (Kabu Verdi)",
40919         "cv",
40920         "238"
40921       ],
40922       [
40923         "Caribbean Netherlands",
40924         "bq",
40925         "599",
40926         1
40927       ],
40928       [
40929         "Cayman Islands",
40930         "ky",
40931         "1345"
40932       ],
40933       [
40934         "Central African Republic (République centrafricaine)",
40935         "cf",
40936         "236"
40937       ],
40938       [
40939         "Chad (Tchad)",
40940         "td",
40941         "235"
40942       ],
40943       [
40944         "Chile",
40945         "cl",
40946         "56"
40947       ],
40948       [
40949         "China (中国)",
40950         "cn",
40951         "86"
40952       ],
40953       [
40954         "Christmas Island",
40955         "cx",
40956         "61",
40957         2
40958       ],
40959       [
40960         "Cocos (Keeling) Islands",
40961         "cc",
40962         "61",
40963         1
40964       ],
40965       [
40966         "Colombia",
40967         "co",
40968         "57"
40969       ],
40970       [
40971         "Comoros (‫جزر القمر‬‎)",
40972         "km",
40973         "269"
40974       ],
40975       [
40976         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
40977         "cd",
40978         "243"
40979       ],
40980       [
40981         "Congo (Republic) (Congo-Brazzaville)",
40982         "cg",
40983         "242"
40984       ],
40985       [
40986         "Cook Islands",
40987         "ck",
40988         "682"
40989       ],
40990       [
40991         "Costa Rica",
40992         "cr",
40993         "506"
40994       ],
40995       [
40996         "Côte d’Ivoire",
40997         "ci",
40998         "225"
40999       ],
41000       [
41001         "Croatia (Hrvatska)",
41002         "hr",
41003         "385"
41004       ],
41005       [
41006         "Cuba",
41007         "cu",
41008         "53"
41009       ],
41010       [
41011         "Curaçao",
41012         "cw",
41013         "599",
41014         0
41015       ],
41016       [
41017         "Cyprus (Κύπρος)",
41018         "cy",
41019         "357"
41020       ],
41021       [
41022         "Czech Republic (Česká republika)",
41023         "cz",
41024         "420"
41025       ],
41026       [
41027         "Denmark (Danmark)",
41028         "dk",
41029         "45"
41030       ],
41031       [
41032         "Djibouti",
41033         "dj",
41034         "253"
41035       ],
41036       [
41037         "Dominica",
41038         "dm",
41039         "1767"
41040       ],
41041       [
41042         "Dominican Republic (República Dominicana)",
41043         "do",
41044         "1",
41045         2,
41046         ["809", "829", "849"]
41047       ],
41048       [
41049         "Ecuador",
41050         "ec",
41051         "593"
41052       ],
41053       [
41054         "Egypt (‫مصر‬‎)",
41055         "eg",
41056         "20"
41057       ],
41058       [
41059         "El Salvador",
41060         "sv",
41061         "503"
41062       ],
41063       [
41064         "Equatorial Guinea (Guinea Ecuatorial)",
41065         "gq",
41066         "240"
41067       ],
41068       [
41069         "Eritrea",
41070         "er",
41071         "291"
41072       ],
41073       [
41074         "Estonia (Eesti)",
41075         "ee",
41076         "372"
41077       ],
41078       [
41079         "Ethiopia",
41080         "et",
41081         "251"
41082       ],
41083       [
41084         "Falkland Islands (Islas Malvinas)",
41085         "fk",
41086         "500"
41087       ],
41088       [
41089         "Faroe Islands (Føroyar)",
41090         "fo",
41091         "298"
41092       ],
41093       [
41094         "Fiji",
41095         "fj",
41096         "679"
41097       ],
41098       [
41099         "Finland (Suomi)",
41100         "fi",
41101         "358",
41102         0
41103       ],
41104       [
41105         "France",
41106         "fr",
41107         "33"
41108       ],
41109       [
41110         "French Guiana (Guyane française)",
41111         "gf",
41112         "594"
41113       ],
41114       [
41115         "French Polynesia (Polynésie française)",
41116         "pf",
41117         "689"
41118       ],
41119       [
41120         "Gabon",
41121         "ga",
41122         "241"
41123       ],
41124       [
41125         "Gambia",
41126         "gm",
41127         "220"
41128       ],
41129       [
41130         "Georgia (საქართველო)",
41131         "ge",
41132         "995"
41133       ],
41134       [
41135         "Germany (Deutschland)",
41136         "de",
41137         "49"
41138       ],
41139       [
41140         "Ghana (Gaana)",
41141         "gh",
41142         "233"
41143       ],
41144       [
41145         "Gibraltar",
41146         "gi",
41147         "350"
41148       ],
41149       [
41150         "Greece (Ελλάδα)",
41151         "gr",
41152         "30"
41153       ],
41154       [
41155         "Greenland (Kalaallit Nunaat)",
41156         "gl",
41157         "299"
41158       ],
41159       [
41160         "Grenada",
41161         "gd",
41162         "1473"
41163       ],
41164       [
41165         "Guadeloupe",
41166         "gp",
41167         "590",
41168         0
41169       ],
41170       [
41171         "Guam",
41172         "gu",
41173         "1671"
41174       ],
41175       [
41176         "Guatemala",
41177         "gt",
41178         "502"
41179       ],
41180       [
41181         "Guernsey",
41182         "gg",
41183         "44",
41184         1
41185       ],
41186       [
41187         "Guinea (Guinée)",
41188         "gn",
41189         "224"
41190       ],
41191       [
41192         "Guinea-Bissau (Guiné Bissau)",
41193         "gw",
41194         "245"
41195       ],
41196       [
41197         "Guyana",
41198         "gy",
41199         "592"
41200       ],
41201       [
41202         "Haiti",
41203         "ht",
41204         "509"
41205       ],
41206       [
41207         "Honduras",
41208         "hn",
41209         "504"
41210       ],
41211       [
41212         "Hong Kong (香港)",
41213         "hk",
41214         "852"
41215       ],
41216       [
41217         "Hungary (Magyarország)",
41218         "hu",
41219         "36"
41220       ],
41221       [
41222         "Iceland (Ísland)",
41223         "is",
41224         "354"
41225       ],
41226       [
41227         "India (भारत)",
41228         "in",
41229         "91"
41230       ],
41231       [
41232         "Indonesia",
41233         "id",
41234         "62"
41235       ],
41236       [
41237         "Iran (‫ایران‬‎)",
41238         "ir",
41239         "98"
41240       ],
41241       [
41242         "Iraq (‫العراق‬‎)",
41243         "iq",
41244         "964"
41245       ],
41246       [
41247         "Ireland",
41248         "ie",
41249         "353"
41250       ],
41251       [
41252         "Isle of Man",
41253         "im",
41254         "44",
41255         2
41256       ],
41257       [
41258         "Israel (‫ישראל‬‎)",
41259         "il",
41260         "972"
41261       ],
41262       [
41263         "Italy (Italia)",
41264         "it",
41265         "39",
41266         0
41267       ],
41268       [
41269         "Jamaica",
41270         "jm",
41271         "1876"
41272       ],
41273       [
41274         "Japan (日本)",
41275         "jp",
41276         "81"
41277       ],
41278       [
41279         "Jersey",
41280         "je",
41281         "44",
41282         3
41283       ],
41284       [
41285         "Jordan (‫الأردن‬‎)",
41286         "jo",
41287         "962"
41288       ],
41289       [
41290         "Kazakhstan (Казахстан)",
41291         "kz",
41292         "7",
41293         1
41294       ],
41295       [
41296         "Kenya",
41297         "ke",
41298         "254"
41299       ],
41300       [
41301         "Kiribati",
41302         "ki",
41303         "686"
41304       ],
41305       [
41306         "Kosovo",
41307         "xk",
41308         "383"
41309       ],
41310       [
41311         "Kuwait (‫الكويت‬‎)",
41312         "kw",
41313         "965"
41314       ],
41315       [
41316         "Kyrgyzstan (Кыргызстан)",
41317         "kg",
41318         "996"
41319       ],
41320       [
41321         "Laos (ລາວ)",
41322         "la",
41323         "856"
41324       ],
41325       [
41326         "Latvia (Latvija)",
41327         "lv",
41328         "371"
41329       ],
41330       [
41331         "Lebanon (‫لبنان‬‎)",
41332         "lb",
41333         "961"
41334       ],
41335       [
41336         "Lesotho",
41337         "ls",
41338         "266"
41339       ],
41340       [
41341         "Liberia",
41342         "lr",
41343         "231"
41344       ],
41345       [
41346         "Libya (‫ليبيا‬‎)",
41347         "ly",
41348         "218"
41349       ],
41350       [
41351         "Liechtenstein",
41352         "li",
41353         "423"
41354       ],
41355       [
41356         "Lithuania (Lietuva)",
41357         "lt",
41358         "370"
41359       ],
41360       [
41361         "Luxembourg",
41362         "lu",
41363         "352"
41364       ],
41365       [
41366         "Macau (澳門)",
41367         "mo",
41368         "853"
41369       ],
41370       [
41371         "Macedonia (FYROM) (Македонија)",
41372         "mk",
41373         "389"
41374       ],
41375       [
41376         "Madagascar (Madagasikara)",
41377         "mg",
41378         "261"
41379       ],
41380       [
41381         "Malawi",
41382         "mw",
41383         "265"
41384       ],
41385       [
41386         "Malaysia",
41387         "my",
41388         "60"
41389       ],
41390       [
41391         "Maldives",
41392         "mv",
41393         "960"
41394       ],
41395       [
41396         "Mali",
41397         "ml",
41398         "223"
41399       ],
41400       [
41401         "Malta",
41402         "mt",
41403         "356"
41404       ],
41405       [
41406         "Marshall Islands",
41407         "mh",
41408         "692"
41409       ],
41410       [
41411         "Martinique",
41412         "mq",
41413         "596"
41414       ],
41415       [
41416         "Mauritania (‫موريتانيا‬‎)",
41417         "mr",
41418         "222"
41419       ],
41420       [
41421         "Mauritius (Moris)",
41422         "mu",
41423         "230"
41424       ],
41425       [
41426         "Mayotte",
41427         "yt",
41428         "262",
41429         1
41430       ],
41431       [
41432         "Mexico (México)",
41433         "mx",
41434         "52"
41435       ],
41436       [
41437         "Micronesia",
41438         "fm",
41439         "691"
41440       ],
41441       [
41442         "Moldova (Republica Moldova)",
41443         "md",
41444         "373"
41445       ],
41446       [
41447         "Monaco",
41448         "mc",
41449         "377"
41450       ],
41451       [
41452         "Mongolia (Монгол)",
41453         "mn",
41454         "976"
41455       ],
41456       [
41457         "Montenegro (Crna Gora)",
41458         "me",
41459         "382"
41460       ],
41461       [
41462         "Montserrat",
41463         "ms",
41464         "1664"
41465       ],
41466       [
41467         "Morocco (‫المغرب‬‎)",
41468         "ma",
41469         "212",
41470         0
41471       ],
41472       [
41473         "Mozambique (Moçambique)",
41474         "mz",
41475         "258"
41476       ],
41477       [
41478         "Myanmar (Burma) (မြန်မာ)",
41479         "mm",
41480         "95"
41481       ],
41482       [
41483         "Namibia (Namibië)",
41484         "na",
41485         "264"
41486       ],
41487       [
41488         "Nauru",
41489         "nr",
41490         "674"
41491       ],
41492       [
41493         "Nepal (नेपाल)",
41494         "np",
41495         "977"
41496       ],
41497       [
41498         "Netherlands (Nederland)",
41499         "nl",
41500         "31"
41501       ],
41502       [
41503         "New Caledonia (Nouvelle-Calédonie)",
41504         "nc",
41505         "687"
41506       ],
41507       [
41508         "New Zealand",
41509         "nz",
41510         "64"
41511       ],
41512       [
41513         "Nicaragua",
41514         "ni",
41515         "505"
41516       ],
41517       [
41518         "Niger (Nijar)",
41519         "ne",
41520         "227"
41521       ],
41522       [
41523         "Nigeria",
41524         "ng",
41525         "234"
41526       ],
41527       [
41528         "Niue",
41529         "nu",
41530         "683"
41531       ],
41532       [
41533         "Norfolk Island",
41534         "nf",
41535         "672"
41536       ],
41537       [
41538         "North Korea (조선 민주주의 인민 공화국)",
41539         "kp",
41540         "850"
41541       ],
41542       [
41543         "Northern Mariana Islands",
41544         "mp",
41545         "1670"
41546       ],
41547       [
41548         "Norway (Norge)",
41549         "no",
41550         "47",
41551         0
41552       ],
41553       [
41554         "Oman (‫عُمان‬‎)",
41555         "om",
41556         "968"
41557       ],
41558       [
41559         "Pakistan (‫پاکستان‬‎)",
41560         "pk",
41561         "92"
41562       ],
41563       [
41564         "Palau",
41565         "pw",
41566         "680"
41567       ],
41568       [
41569         "Palestine (‫فلسطين‬‎)",
41570         "ps",
41571         "970"
41572       ],
41573       [
41574         "Panama (Panamá)",
41575         "pa",
41576         "507"
41577       ],
41578       [
41579         "Papua New Guinea",
41580         "pg",
41581         "675"
41582       ],
41583       [
41584         "Paraguay",
41585         "py",
41586         "595"
41587       ],
41588       [
41589         "Peru (Perú)",
41590         "pe",
41591         "51"
41592       ],
41593       [
41594         "Philippines",
41595         "ph",
41596         "63"
41597       ],
41598       [
41599         "Poland (Polska)",
41600         "pl",
41601         "48"
41602       ],
41603       [
41604         "Portugal",
41605         "pt",
41606         "351"
41607       ],
41608       [
41609         "Puerto Rico",
41610         "pr",
41611         "1",
41612         3,
41613         ["787", "939"]
41614       ],
41615       [
41616         "Qatar (‫قطر‬‎)",
41617         "qa",
41618         "974"
41619       ],
41620       [
41621         "Réunion (La Réunion)",
41622         "re",
41623         "262",
41624         0
41625       ],
41626       [
41627         "Romania (România)",
41628         "ro",
41629         "40"
41630       ],
41631       [
41632         "Russia (Россия)",
41633         "ru",
41634         "7",
41635         0
41636       ],
41637       [
41638         "Rwanda",
41639         "rw",
41640         "250"
41641       ],
41642       [
41643         "Saint Barthélemy",
41644         "bl",
41645         "590",
41646         1
41647       ],
41648       [
41649         "Saint Helena",
41650         "sh",
41651         "290"
41652       ],
41653       [
41654         "Saint Kitts and Nevis",
41655         "kn",
41656         "1869"
41657       ],
41658       [
41659         "Saint Lucia",
41660         "lc",
41661         "1758"
41662       ],
41663       [
41664         "Saint Martin (Saint-Martin (partie française))",
41665         "mf",
41666         "590",
41667         2
41668       ],
41669       [
41670         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41671         "pm",
41672         "508"
41673       ],
41674       [
41675         "Saint Vincent and the Grenadines",
41676         "vc",
41677         "1784"
41678       ],
41679       [
41680         "Samoa",
41681         "ws",
41682         "685"
41683       ],
41684       [
41685         "San Marino",
41686         "sm",
41687         "378"
41688       ],
41689       [
41690         "São Tomé and Príncipe (São Tomé e Príncipe)",
41691         "st",
41692         "239"
41693       ],
41694       [
41695         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41696         "sa",
41697         "966"
41698       ],
41699       [
41700         "Senegal (Sénégal)",
41701         "sn",
41702         "221"
41703       ],
41704       [
41705         "Serbia (Србија)",
41706         "rs",
41707         "381"
41708       ],
41709       [
41710         "Seychelles",
41711         "sc",
41712         "248"
41713       ],
41714       [
41715         "Sierra Leone",
41716         "sl",
41717         "232"
41718       ],
41719       [
41720         "Singapore",
41721         "sg",
41722         "65"
41723       ],
41724       [
41725         "Sint Maarten",
41726         "sx",
41727         "1721"
41728       ],
41729       [
41730         "Slovakia (Slovensko)",
41731         "sk",
41732         "421"
41733       ],
41734       [
41735         "Slovenia (Slovenija)",
41736         "si",
41737         "386"
41738       ],
41739       [
41740         "Solomon Islands",
41741         "sb",
41742         "677"
41743       ],
41744       [
41745         "Somalia (Soomaaliya)",
41746         "so",
41747         "252"
41748       ],
41749       [
41750         "South Africa",
41751         "za",
41752         "27"
41753       ],
41754       [
41755         "South Korea (대한민국)",
41756         "kr",
41757         "82"
41758       ],
41759       [
41760         "South Sudan (‫جنوب السودان‬‎)",
41761         "ss",
41762         "211"
41763       ],
41764       [
41765         "Spain (España)",
41766         "es",
41767         "34"
41768       ],
41769       [
41770         "Sri Lanka (ශ්‍රී ලංකාව)",
41771         "lk",
41772         "94"
41773       ],
41774       [
41775         "Sudan (‫السودان‬‎)",
41776         "sd",
41777         "249"
41778       ],
41779       [
41780         "Suriname",
41781         "sr",
41782         "597"
41783       ],
41784       [
41785         "Svalbard and Jan Mayen",
41786         "sj",
41787         "47",
41788         1
41789       ],
41790       [
41791         "Swaziland",
41792         "sz",
41793         "268"
41794       ],
41795       [
41796         "Sweden (Sverige)",
41797         "se",
41798         "46"
41799       ],
41800       [
41801         "Switzerland (Schweiz)",
41802         "ch",
41803         "41"
41804       ],
41805       [
41806         "Syria (‫سوريا‬‎)",
41807         "sy",
41808         "963"
41809       ],
41810       [
41811         "Taiwan (台灣)",
41812         "tw",
41813         "886"
41814       ],
41815       [
41816         "Tajikistan",
41817         "tj",
41818         "992"
41819       ],
41820       [
41821         "Tanzania",
41822         "tz",
41823         "255"
41824       ],
41825       [
41826         "Thailand (ไทย)",
41827         "th",
41828         "66"
41829       ],
41830       [
41831         "Timor-Leste",
41832         "tl",
41833         "670"
41834       ],
41835       [
41836         "Togo",
41837         "tg",
41838         "228"
41839       ],
41840       [
41841         "Tokelau",
41842         "tk",
41843         "690"
41844       ],
41845       [
41846         "Tonga",
41847         "to",
41848         "676"
41849       ],
41850       [
41851         "Trinidad and Tobago",
41852         "tt",
41853         "1868"
41854       ],
41855       [
41856         "Tunisia (‫تونس‬‎)",
41857         "tn",
41858         "216"
41859       ],
41860       [
41861         "Turkey (Türkiye)",
41862         "tr",
41863         "90"
41864       ],
41865       [
41866         "Turkmenistan",
41867         "tm",
41868         "993"
41869       ],
41870       [
41871         "Turks and Caicos Islands",
41872         "tc",
41873         "1649"
41874       ],
41875       [
41876         "Tuvalu",
41877         "tv",
41878         "688"
41879       ],
41880       [
41881         "U.S. Virgin Islands",
41882         "vi",
41883         "1340"
41884       ],
41885       [
41886         "Uganda",
41887         "ug",
41888         "256"
41889       ],
41890       [
41891         "Ukraine (Україна)",
41892         "ua",
41893         "380"
41894       ],
41895       [
41896         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
41897         "ae",
41898         "971"
41899       ],
41900       [
41901         "United Kingdom",
41902         "gb",
41903         "44",
41904         0
41905       ],
41906       [
41907         "United States",
41908         "us",
41909         "1",
41910         0
41911       ],
41912       [
41913         "Uruguay",
41914         "uy",
41915         "598"
41916       ],
41917       [
41918         "Uzbekistan (Oʻzbekiston)",
41919         "uz",
41920         "998"
41921       ],
41922       [
41923         "Vanuatu",
41924         "vu",
41925         "678"
41926       ],
41927       [
41928         "Vatican City (Città del Vaticano)",
41929         "va",
41930         "39",
41931         1
41932       ],
41933       [
41934         "Venezuela",
41935         "ve",
41936         "58"
41937       ],
41938       [
41939         "Vietnam (Việt Nam)",
41940         "vn",
41941         "84"
41942       ],
41943       [
41944         "Wallis and Futuna (Wallis-et-Futuna)",
41945         "wf",
41946         "681"
41947       ],
41948       [
41949         "Western Sahara (‫الصحراء الغربية‬‎)",
41950         "eh",
41951         "212",
41952         1
41953       ],
41954       [
41955         "Yemen (‫اليمن‬‎)",
41956         "ye",
41957         "967"
41958       ],
41959       [
41960         "Zambia",
41961         "zm",
41962         "260"
41963       ],
41964       [
41965         "Zimbabwe",
41966         "zw",
41967         "263"
41968       ],
41969       [
41970         "Åland Islands",
41971         "ax",
41972         "358",
41973         1
41974       ]
41975   ];
41976   
41977   return d;
41978 }/**
41979 *    This script refer to:
41980 *    Title: International Telephone Input
41981 *    Author: Jack O'Connor
41982 *    Code version:  v12.1.12
41983 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41984 **/
41985
41986 /**
41987  * @class Roo.bootstrap.PhoneInput
41988  * @extends Roo.bootstrap.TriggerField
41989  * An input with International dial-code selection
41990  
41991  * @cfg {String} defaultDialCode default '+852'
41992  * @cfg {Array} preferedCountries default []
41993   
41994  * @constructor
41995  * Create a new PhoneInput.
41996  * @param {Object} config Configuration options
41997  */
41998
41999 Roo.bootstrap.PhoneInput = function(config) {
42000     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42001 };
42002
42003 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42004         
42005         listWidth: undefined,
42006         
42007         selectedClass: 'active',
42008         
42009         invalidClass : "has-warning",
42010         
42011         validClass: 'has-success',
42012         
42013         allowed: '0123456789',
42014         
42015         max_length: 15,
42016         
42017         /**
42018          * @cfg {String} defaultDialCode The default dial code when initializing the input
42019          */
42020         defaultDialCode: '+852',
42021         
42022         /**
42023          * @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
42024          */
42025         preferedCountries: false,
42026         
42027         getAutoCreate : function()
42028         {
42029             var data = Roo.bootstrap.PhoneInputData();
42030             var align = this.labelAlign || this.parentLabelAlign();
42031             var id = Roo.id();
42032             
42033             this.allCountries = [];
42034             this.dialCodeMapping = [];
42035             
42036             for (var i = 0; i < data.length; i++) {
42037               var c = data[i];
42038               this.allCountries[i] = {
42039                 name: c[0],
42040                 iso2: c[1],
42041                 dialCode: c[2],
42042                 priority: c[3] || 0,
42043                 areaCodes: c[4] || null
42044               };
42045               this.dialCodeMapping[c[2]] = {
42046                   name: c[0],
42047                   iso2: c[1],
42048                   priority: c[3] || 0,
42049                   areaCodes: c[4] || null
42050               };
42051             }
42052             
42053             var cfg = {
42054                 cls: 'form-group',
42055                 cn: []
42056             };
42057             
42058             var input =  {
42059                 tag: 'input',
42060                 id : id,
42061                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42062                 maxlength: this.max_length,
42063                 cls : 'form-control tel-input',
42064                 autocomplete: 'new-password'
42065             };
42066             
42067             var hiddenInput = {
42068                 tag: 'input',
42069                 type: 'hidden',
42070                 cls: 'hidden-tel-input'
42071             };
42072             
42073             if (this.name) {
42074                 hiddenInput.name = this.name;
42075             }
42076             
42077             if (this.disabled) {
42078                 input.disabled = true;
42079             }
42080             
42081             var flag_container = {
42082                 tag: 'div',
42083                 cls: 'flag-box',
42084                 cn: [
42085                     {
42086                         tag: 'div',
42087                         cls: 'flag'
42088                     },
42089                     {
42090                         tag: 'div',
42091                         cls: 'caret'
42092                     }
42093                 ]
42094             };
42095             
42096             var box = {
42097                 tag: 'div',
42098                 cls: this.hasFeedback ? 'has-feedback' : '',
42099                 cn: [
42100                     hiddenInput,
42101                     input,
42102                     {
42103                         tag: 'input',
42104                         cls: 'dial-code-holder',
42105                         disabled: true
42106                     }
42107                 ]
42108             };
42109             
42110             var container = {
42111                 cls: 'roo-select2-container input-group',
42112                 cn: [
42113                     flag_container,
42114                     box
42115                 ]
42116             };
42117             
42118             if (this.fieldLabel.length) {
42119                 var indicator = {
42120                     tag: 'i',
42121                     tooltip: 'This field is required'
42122                 };
42123                 
42124                 var label = {
42125                     tag: 'label',
42126                     'for':  id,
42127                     cls: 'control-label',
42128                     cn: []
42129                 };
42130                 
42131                 var label_text = {
42132                     tag: 'span',
42133                     html: this.fieldLabel
42134                 };
42135                 
42136                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42137                 label.cn = [
42138                     indicator,
42139                     label_text
42140                 ];
42141                 
42142                 if(this.indicatorpos == 'right') {
42143                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42144                     label.cn = [
42145                         label_text,
42146                         indicator
42147                     ];
42148                 }
42149                 
42150                 if(align == 'left') {
42151                     container = {
42152                         tag: 'div',
42153                         cn: [
42154                             container
42155                         ]
42156                     };
42157                     
42158                     if(this.labelWidth > 12){
42159                         label.style = "width: " + this.labelWidth + 'px';
42160                     }
42161                     if(this.labelWidth < 13 && this.labelmd == 0){
42162                         this.labelmd = this.labelWidth;
42163                     }
42164                     if(this.labellg > 0){
42165                         label.cls += ' col-lg-' + this.labellg;
42166                         input.cls += ' col-lg-' + (12 - this.labellg);
42167                     }
42168                     if(this.labelmd > 0){
42169                         label.cls += ' col-md-' + this.labelmd;
42170                         container.cls += ' col-md-' + (12 - this.labelmd);
42171                     }
42172                     if(this.labelsm > 0){
42173                         label.cls += ' col-sm-' + this.labelsm;
42174                         container.cls += ' col-sm-' + (12 - this.labelsm);
42175                     }
42176                     if(this.labelxs > 0){
42177                         label.cls += ' col-xs-' + this.labelxs;
42178                         container.cls += ' col-xs-' + (12 - this.labelxs);
42179                     }
42180                 }
42181             }
42182             
42183             cfg.cn = [
42184                 label,
42185                 container
42186             ];
42187             
42188             var settings = this;
42189             
42190             ['xs','sm','md','lg'].map(function(size){
42191                 if (settings[size]) {
42192                     cfg.cls += ' col-' + size + '-' + settings[size];
42193                 }
42194             });
42195             
42196             this.store = new Roo.data.Store({
42197                 proxy : new Roo.data.MemoryProxy({}),
42198                 reader : new Roo.data.JsonReader({
42199                     fields : [
42200                         {
42201                             'name' : 'name',
42202                             'type' : 'string'
42203                         },
42204                         {
42205                             'name' : 'iso2',
42206                             'type' : 'string'
42207                         },
42208                         {
42209                             'name' : 'dialCode',
42210                             'type' : 'string'
42211                         },
42212                         {
42213                             'name' : 'priority',
42214                             'type' : 'string'
42215                         },
42216                         {
42217                             'name' : 'areaCodes',
42218                             'type' : 'string'
42219                         }
42220                     ]
42221                 })
42222             });
42223             
42224             if(!this.preferedCountries) {
42225                 this.preferedCountries = [
42226                     'hk',
42227                     'gb',
42228                     'us'
42229                 ];
42230             }
42231             
42232             var p = this.preferedCountries.reverse();
42233             
42234             if(p) {
42235                 for (var i = 0; i < p.length; i++) {
42236                     for (var j = 0; j < this.allCountries.length; j++) {
42237                         if(this.allCountries[j].iso2 == p[i]) {
42238                             var t = this.allCountries[j];
42239                             this.allCountries.splice(j,1);
42240                             this.allCountries.unshift(t);
42241                         }
42242                     } 
42243                 }
42244             }
42245             
42246             this.store.proxy.data = {
42247                 success: true,
42248                 data: this.allCountries
42249             };
42250             
42251             return cfg;
42252         },
42253         
42254         initEvents : function()
42255         {
42256             this.createList();
42257             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42258             
42259             this.indicator = this.indicatorEl();
42260             this.flag = this.flagEl();
42261             this.dialCodeHolder = this.dialCodeHolderEl();
42262             
42263             this.trigger = this.el.select('div.flag-box',true).first();
42264             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42265             
42266             var _this = this;
42267             
42268             (function(){
42269                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42270                 _this.list.setWidth(lw);
42271             }).defer(100);
42272             
42273             this.list.on('mouseover', this.onViewOver, this);
42274             this.list.on('mousemove', this.onViewMove, this);
42275             this.inputEl().on("keyup", this.onKeyUp, this);
42276             this.inputEl().on("keypress", this.onKeyPress, this);
42277             
42278             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42279
42280             this.view = new Roo.View(this.list, this.tpl, {
42281                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42282             });
42283             
42284             this.view.on('click', this.onViewClick, this);
42285             this.setValue(this.defaultDialCode);
42286         },
42287         
42288         onTriggerClick : function(e)
42289         {
42290             Roo.log('trigger click');
42291             if(this.disabled){
42292                 return;
42293             }
42294             
42295             if(this.isExpanded()){
42296                 this.collapse();
42297                 this.hasFocus = false;
42298             }else {
42299                 this.store.load({});
42300                 this.hasFocus = true;
42301                 this.expand();
42302             }
42303         },
42304         
42305         isExpanded : function()
42306         {
42307             return this.list.isVisible();
42308         },
42309         
42310         collapse : function()
42311         {
42312             if(!this.isExpanded()){
42313                 return;
42314             }
42315             this.list.hide();
42316             Roo.get(document).un('mousedown', this.collapseIf, this);
42317             Roo.get(document).un('mousewheel', this.collapseIf, this);
42318             this.fireEvent('collapse', this);
42319             this.validate();
42320         },
42321         
42322         expand : function()
42323         {
42324             Roo.log('expand');
42325
42326             if(this.isExpanded() || !this.hasFocus){
42327                 return;
42328             }
42329             
42330             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42331             this.list.setWidth(lw);
42332             
42333             this.list.show();
42334             this.restrictHeight();
42335             
42336             Roo.get(document).on('mousedown', this.collapseIf, this);
42337             Roo.get(document).on('mousewheel', this.collapseIf, this);
42338             
42339             this.fireEvent('expand', this);
42340         },
42341         
42342         restrictHeight : function()
42343         {
42344             this.list.alignTo(this.inputEl(), this.listAlign);
42345             this.list.alignTo(this.inputEl(), this.listAlign);
42346         },
42347         
42348         onViewOver : function(e, t)
42349         {
42350             if(this.inKeyMode){
42351                 return;
42352             }
42353             var item = this.view.findItemFromChild(t);
42354             
42355             if(item){
42356                 var index = this.view.indexOf(item);
42357                 this.select(index, false);
42358             }
42359         },
42360
42361         // private
42362         onViewClick : function(view, doFocus, el, e)
42363         {
42364             var index = this.view.getSelectedIndexes()[0];
42365             
42366             var r = this.store.getAt(index);
42367             
42368             if(r){
42369                 this.onSelect(r, index);
42370             }
42371             if(doFocus !== false && !this.blockFocus){
42372                 this.inputEl().focus();
42373             }
42374         },
42375         
42376         onViewMove : function(e, t)
42377         {
42378             this.inKeyMode = false;
42379         },
42380         
42381         select : function(index, scrollIntoView)
42382         {
42383             this.selectedIndex = index;
42384             this.view.select(index);
42385             if(scrollIntoView !== false){
42386                 var el = this.view.getNode(index);
42387                 if(el){
42388                     this.list.scrollChildIntoView(el, false);
42389                 }
42390             }
42391         },
42392         
42393         createList : function()
42394         {
42395             this.list = Roo.get(document.body).createChild({
42396                 tag: 'ul',
42397                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42398                 style: 'display:none'
42399             });
42400             
42401             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42402         },
42403         
42404         collapseIf : function(e)
42405         {
42406             var in_combo  = e.within(this.el);
42407             var in_list =  e.within(this.list);
42408             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42409             
42410             if (in_combo || in_list || is_list) {
42411                 return;
42412             }
42413             this.collapse();
42414         },
42415         
42416         onSelect : function(record, index)
42417         {
42418             if(this.fireEvent('beforeselect', this, record, index) !== false){
42419                 
42420                 this.setFlagClass(record.data.iso2);
42421                 this.setDialCode(record.data.dialCode);
42422                 this.hasFocus = false;
42423                 this.collapse();
42424                 this.fireEvent('select', this, record, index);
42425             }
42426         },
42427         
42428         flagEl : function()
42429         {
42430             var flag = this.el.select('div.flag',true).first();
42431             if(!flag){
42432                 return false;
42433             }
42434             return flag;
42435         },
42436         
42437         dialCodeHolderEl : function()
42438         {
42439             var d = this.el.select('input.dial-code-holder',true).first();
42440             if(!d){
42441                 return false;
42442             }
42443             return d;
42444         },
42445         
42446         setDialCode : function(v)
42447         {
42448             this.dialCodeHolder.dom.value = '+'+v;
42449         },
42450         
42451         setFlagClass : function(n)
42452         {
42453             this.flag.dom.className = 'flag '+n;
42454         },
42455         
42456         getValue : function()
42457         {
42458             var v = this.inputEl().getValue();
42459             if(this.dialCodeHolder) {
42460                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42461             }
42462             return v;
42463         },
42464         
42465         setValue : function(v)
42466         {
42467             var d = this.getDialCode(v);
42468             
42469             //invalid dial code
42470             if(v.length == 0 || !d || d.length == 0) {
42471                 if(this.rendered){
42472                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42473                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42474                 }
42475                 return;
42476             }
42477             
42478             //valid dial code
42479             this.setFlagClass(this.dialCodeMapping[d].iso2);
42480             this.setDialCode(d);
42481             this.inputEl().dom.value = v.replace('+'+d,'');
42482             this.hiddenEl().dom.value = this.getValue();
42483             
42484             this.validate();
42485         },
42486         
42487         getDialCode : function(v)
42488         {
42489             v = v ||  '';
42490             
42491             if (v.length == 0) {
42492                 return this.dialCodeHolder.dom.value;
42493             }
42494             
42495             var dialCode = "";
42496             if (v.charAt(0) != "+") {
42497                 return false;
42498             }
42499             var numericChars = "";
42500             for (var i = 1; i < v.length; i++) {
42501               var c = v.charAt(i);
42502               if (!isNaN(c)) {
42503                 numericChars += c;
42504                 if (this.dialCodeMapping[numericChars]) {
42505                   dialCode = v.substr(1, i);
42506                 }
42507                 if (numericChars.length == 4) {
42508                   break;
42509                 }
42510               }
42511             }
42512             return dialCode;
42513         },
42514         
42515         reset : function()
42516         {
42517             this.setValue(this.defaultDialCode);
42518             this.validate();
42519         },
42520         
42521         hiddenEl : function()
42522         {
42523             return this.el.select('input.hidden-tel-input',true).first();
42524         },
42525         
42526         // after setting val
42527         onKeyUp : function(e){
42528             this.setValue(this.getValue());
42529         },
42530         
42531         onKeyPress : function(e){
42532             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42533                 e.stopEvent();
42534             }
42535         }
42536         
42537 });
42538 /**
42539  * @class Roo.bootstrap.MoneyField
42540  * @extends Roo.bootstrap.ComboBox
42541  * Bootstrap MoneyField class
42542  * 
42543  * @constructor
42544  * Create a new MoneyField.
42545  * @param {Object} config Configuration options
42546  */
42547
42548 Roo.bootstrap.MoneyField = function(config) {
42549     
42550     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42551     
42552 };
42553
42554 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42555     
42556     /**
42557      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42558      */
42559     allowDecimals : true,
42560     /**
42561      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42562      */
42563     decimalSeparator : ".",
42564     /**
42565      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42566      */
42567     decimalPrecision : 0,
42568     /**
42569      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42570      */
42571     allowNegative : true,
42572     /**
42573      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42574      */
42575     allowZero: true,
42576     /**
42577      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42578      */
42579     minValue : Number.NEGATIVE_INFINITY,
42580     /**
42581      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42582      */
42583     maxValue : Number.MAX_VALUE,
42584     /**
42585      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42586      */
42587     minText : "The minimum value for this field is {0}",
42588     /**
42589      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42590      */
42591     maxText : "The maximum value for this field is {0}",
42592     /**
42593      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42594      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42595      */
42596     nanText : "{0} is not a valid number",
42597     /**
42598      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42599      */
42600     castInt : true,
42601     /**
42602      * @cfg {String} defaults currency of the MoneyField
42603      * value should be in lkey
42604      */
42605     defaultCurrency : false,
42606     /**
42607      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42608      */
42609     thousandsDelimiter : false,
42610     /**
42611      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42612      */
42613     max_length: false,
42614     
42615     inputlg : 9,
42616     inputmd : 9,
42617     inputsm : 9,
42618     inputxs : 6,
42619     
42620     store : false,
42621     
42622     getAutoCreate : function()
42623     {
42624         var align = this.labelAlign || this.parentLabelAlign();
42625         
42626         var id = Roo.id();
42627
42628         var cfg = {
42629             cls: 'form-group',
42630             cn: []
42631         };
42632
42633         var input =  {
42634             tag: 'input',
42635             id : id,
42636             cls : 'form-control roo-money-amount-input',
42637             autocomplete: 'new-password'
42638         };
42639         
42640         var hiddenInput = {
42641             tag: 'input',
42642             type: 'hidden',
42643             id: Roo.id(),
42644             cls: 'hidden-number-input'
42645         };
42646         
42647         if(this.max_length) {
42648             input.maxlength = this.max_length; 
42649         }
42650         
42651         if (this.name) {
42652             hiddenInput.name = this.name;
42653         }
42654
42655         if (this.disabled) {
42656             input.disabled = true;
42657         }
42658
42659         var clg = 12 - this.inputlg;
42660         var cmd = 12 - this.inputmd;
42661         var csm = 12 - this.inputsm;
42662         var cxs = 12 - this.inputxs;
42663         
42664         var container = {
42665             tag : 'div',
42666             cls : 'row roo-money-field',
42667             cn : [
42668                 {
42669                     tag : 'div',
42670                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42671                     cn : [
42672                         {
42673                             tag : 'div',
42674                             cls: 'roo-select2-container input-group',
42675                             cn: [
42676                                 {
42677                                     tag : 'input',
42678                                     cls : 'form-control roo-money-currency-input',
42679                                     autocomplete: 'new-password',
42680                                     readOnly : 1,
42681                                     name : this.currencyName
42682                                 },
42683                                 {
42684                                     tag :'span',
42685                                     cls : 'input-group-addon',
42686                                     cn : [
42687                                         {
42688                                             tag: 'span',
42689                                             cls: 'caret'
42690                                         }
42691                                     ]
42692                                 }
42693                             ]
42694                         }
42695                     ]
42696                 },
42697                 {
42698                     tag : 'div',
42699                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42700                     cn : [
42701                         {
42702                             tag: 'div',
42703                             cls: this.hasFeedback ? 'has-feedback' : '',
42704                             cn: [
42705                                 input
42706                             ]
42707                         }
42708                     ]
42709                 }
42710             ]
42711             
42712         };
42713         
42714         if (this.fieldLabel.length) {
42715             var indicator = {
42716                 tag: 'i',
42717                 tooltip: 'This field is required'
42718             };
42719
42720             var label = {
42721                 tag: 'label',
42722                 'for':  id,
42723                 cls: 'control-label',
42724                 cn: []
42725             };
42726
42727             var label_text = {
42728                 tag: 'span',
42729                 html: this.fieldLabel
42730             };
42731
42732             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42733             label.cn = [
42734                 indicator,
42735                 label_text
42736             ];
42737
42738             if(this.indicatorpos == 'right') {
42739                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42740                 label.cn = [
42741                     label_text,
42742                     indicator
42743                 ];
42744             }
42745
42746             if(align == 'left') {
42747                 container = {
42748                     tag: 'div',
42749                     cn: [
42750                         container
42751                     ]
42752                 };
42753
42754                 if(this.labelWidth > 12){
42755                     label.style = "width: " + this.labelWidth + 'px';
42756                 }
42757                 if(this.labelWidth < 13 && this.labelmd == 0){
42758                     this.labelmd = this.labelWidth;
42759                 }
42760                 if(this.labellg > 0){
42761                     label.cls += ' col-lg-' + this.labellg;
42762                     input.cls += ' col-lg-' + (12 - this.labellg);
42763                 }
42764                 if(this.labelmd > 0){
42765                     label.cls += ' col-md-' + this.labelmd;
42766                     container.cls += ' col-md-' + (12 - this.labelmd);
42767                 }
42768                 if(this.labelsm > 0){
42769                     label.cls += ' col-sm-' + this.labelsm;
42770                     container.cls += ' col-sm-' + (12 - this.labelsm);
42771                 }
42772                 if(this.labelxs > 0){
42773                     label.cls += ' col-xs-' + this.labelxs;
42774                     container.cls += ' col-xs-' + (12 - this.labelxs);
42775                 }
42776             }
42777         }
42778
42779         cfg.cn = [
42780             label,
42781             container,
42782             hiddenInput
42783         ];
42784         
42785         var settings = this;
42786
42787         ['xs','sm','md','lg'].map(function(size){
42788             if (settings[size]) {
42789                 cfg.cls += ' col-' + size + '-' + settings[size];
42790             }
42791         });
42792         
42793         return cfg;
42794     },
42795     
42796     initEvents : function()
42797     {
42798         this.indicator = this.indicatorEl();
42799         
42800         this.initCurrencyEvent();
42801         
42802         this.initNumberEvent();
42803     },
42804     
42805     initCurrencyEvent : function()
42806     {
42807         if (!this.store) {
42808             throw "can not find store for combo";
42809         }
42810         
42811         this.store = Roo.factory(this.store, Roo.data);
42812         this.store.parent = this;
42813         
42814         this.createList();
42815         
42816         this.triggerEl = this.el.select('.input-group-addon', true).first();
42817         
42818         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42819         
42820         var _this = this;
42821         
42822         (function(){
42823             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42824             _this.list.setWidth(lw);
42825         }).defer(100);
42826         
42827         this.list.on('mouseover', this.onViewOver, this);
42828         this.list.on('mousemove', this.onViewMove, this);
42829         this.list.on('scroll', this.onViewScroll, this);
42830         
42831         if(!this.tpl){
42832             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42833         }
42834         
42835         this.view = new Roo.View(this.list, this.tpl, {
42836             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42837         });
42838         
42839         this.view.on('click', this.onViewClick, this);
42840         
42841         this.store.on('beforeload', this.onBeforeLoad, this);
42842         this.store.on('load', this.onLoad, this);
42843         this.store.on('loadexception', this.onLoadException, this);
42844         
42845         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42846             "up" : function(e){
42847                 this.inKeyMode = true;
42848                 this.selectPrev();
42849             },
42850
42851             "down" : function(e){
42852                 if(!this.isExpanded()){
42853                     this.onTriggerClick();
42854                 }else{
42855                     this.inKeyMode = true;
42856                     this.selectNext();
42857                 }
42858             },
42859
42860             "enter" : function(e){
42861                 this.collapse();
42862                 
42863                 if(this.fireEvent("specialkey", this, e)){
42864                     this.onViewClick(false);
42865                 }
42866                 
42867                 return true;
42868             },
42869
42870             "esc" : function(e){
42871                 this.collapse();
42872             },
42873
42874             "tab" : function(e){
42875                 this.collapse();
42876                 
42877                 if(this.fireEvent("specialkey", this, e)){
42878                     this.onViewClick(false);
42879                 }
42880                 
42881                 return true;
42882             },
42883
42884             scope : this,
42885
42886             doRelay : function(foo, bar, hname){
42887                 if(hname == 'down' || this.scope.isExpanded()){
42888                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42889                 }
42890                 return true;
42891             },
42892
42893             forceKeyDown: true
42894         });
42895         
42896         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
42897         
42898     },
42899     
42900     initNumberEvent : function(e)
42901     {
42902         this.inputEl().on("keydown" , this.fireKey,  this);
42903         this.inputEl().on("focus", this.onFocus,  this);
42904         this.inputEl().on("blur", this.onBlur,  this);
42905         
42906         this.inputEl().relayEvent('keyup', this);
42907         
42908         if(this.indicator){
42909             this.indicator.addClass('invisible');
42910         }
42911  
42912         this.originalValue = this.getValue();
42913         
42914         if(this.validationEvent == 'keyup'){
42915             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
42916             this.inputEl().on('keyup', this.filterValidation, this);
42917         }
42918         else if(this.validationEvent !== false){
42919             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
42920         }
42921         
42922         if(this.selectOnFocus){
42923             this.on("focus", this.preFocus, this);
42924             
42925         }
42926         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
42927             this.inputEl().on("keypress", this.filterKeys, this);
42928         } else {
42929             this.inputEl().relayEvent('keypress', this);
42930         }
42931         
42932         var allowed = "0123456789";
42933         
42934         if(this.allowDecimals){
42935             allowed += this.decimalSeparator;
42936         }
42937         
42938         if(this.allowNegative){
42939             allowed += "-";
42940         }
42941         
42942         if(this.thousandsDelimiter) {
42943             allowed += ",";
42944         }
42945         
42946         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
42947         
42948         var keyPress = function(e){
42949             
42950             var k = e.getKey();
42951             
42952             var c = e.getCharCode();
42953             
42954             if(
42955                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
42956                     allowed.indexOf(String.fromCharCode(c)) === -1
42957             ){
42958                 e.stopEvent();
42959                 return;
42960             }
42961             
42962             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
42963                 return;
42964             }
42965             
42966             if(allowed.indexOf(String.fromCharCode(c)) === -1){
42967                 e.stopEvent();
42968             }
42969         };
42970         
42971         this.inputEl().on("keypress", keyPress, this);
42972         
42973     },
42974     
42975     onTriggerClick : function(e)
42976     {   
42977         if(this.disabled){
42978             return;
42979         }
42980         
42981         this.page = 0;
42982         this.loadNext = false;
42983         
42984         if(this.isExpanded()){
42985             this.collapse();
42986             return;
42987         }
42988         
42989         this.hasFocus = true;
42990         
42991         if(this.triggerAction == 'all') {
42992             this.doQuery(this.allQuery, true);
42993             return;
42994         }
42995         
42996         this.doQuery(this.getRawValue());
42997     },
42998     
42999     getCurrency : function()
43000     {   
43001         var v = this.currencyEl().getValue();
43002         
43003         return v;
43004     },
43005     
43006     restrictHeight : function()
43007     {
43008         this.list.alignTo(this.currencyEl(), this.listAlign);
43009         this.list.alignTo(this.currencyEl(), this.listAlign);
43010     },
43011     
43012     onViewClick : function(view, doFocus, el, e)
43013     {
43014         var index = this.view.getSelectedIndexes()[0];
43015         
43016         var r = this.store.getAt(index);
43017         
43018         if(r){
43019             this.onSelect(r, index);
43020         }
43021     },
43022     
43023     onSelect : function(record, index){
43024         
43025         if(this.fireEvent('beforeselect', this, record, index) !== false){
43026         
43027             this.setFromCurrencyData(index > -1 ? record.data : false);
43028             
43029             this.collapse();
43030             
43031             this.fireEvent('select', this, record, index);
43032         }
43033     },
43034     
43035     setFromCurrencyData : function(o)
43036     {
43037         var currency = '';
43038         
43039         this.lastCurrency = o;
43040         
43041         if (this.currencyField) {
43042             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43043         } else {
43044             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43045         }
43046         
43047         this.lastSelectionText = currency;
43048         
43049         //setting default currency
43050         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43051             this.setCurrency(this.defaultCurrency);
43052             return;
43053         }
43054         
43055         this.setCurrency(currency);
43056     },
43057     
43058     setFromData : function(o)
43059     {
43060         var c = {};
43061         
43062         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43063         
43064         this.setFromCurrencyData(c);
43065         
43066         var value = '';
43067         
43068         if (this.name) {
43069             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43070         } else {
43071             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43072         }
43073         
43074         this.setValue(value);
43075         
43076     },
43077     
43078     setCurrency : function(v)
43079     {   
43080         this.currencyValue = v;
43081         
43082         if(this.rendered){
43083             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43084             this.validate();
43085         }
43086     },
43087     
43088     setValue : function(v)
43089     {
43090         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43091         
43092         this.value = v;
43093         
43094         if(this.rendered){
43095             
43096             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43097             
43098             this.inputEl().dom.value = (v == '') ? '' :
43099                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43100             
43101             if(!this.allowZero && v === '0') {
43102                 this.hiddenEl().dom.value = '';
43103                 this.inputEl().dom.value = '';
43104             }
43105             
43106             this.validate();
43107         }
43108     },
43109     
43110     getRawValue : function()
43111     {
43112         var v = this.inputEl().getValue();
43113         
43114         return v;
43115     },
43116     
43117     getValue : function()
43118     {
43119         return this.fixPrecision(this.parseValue(this.getRawValue()));
43120     },
43121     
43122     parseValue : function(value)
43123     {
43124         if(this.thousandsDelimiter) {
43125             value += "";
43126             r = new RegExp(",", "g");
43127             value = value.replace(r, "");
43128         }
43129         
43130         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43131         return isNaN(value) ? '' : value;
43132         
43133     },
43134     
43135     fixPrecision : function(value)
43136     {
43137         if(this.thousandsDelimiter) {
43138             value += "";
43139             r = new RegExp(",", "g");
43140             value = value.replace(r, "");
43141         }
43142         
43143         var nan = isNaN(value);
43144         
43145         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43146             return nan ? '' : value;
43147         }
43148         return parseFloat(value).toFixed(this.decimalPrecision);
43149     },
43150     
43151     decimalPrecisionFcn : function(v)
43152     {
43153         return Math.floor(v);
43154     },
43155     
43156     validateValue : function(value)
43157     {
43158         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43159             return false;
43160         }
43161         
43162         var num = this.parseValue(value);
43163         
43164         if(isNaN(num)){
43165             this.markInvalid(String.format(this.nanText, value));
43166             return false;
43167         }
43168         
43169         if(num < this.minValue){
43170             this.markInvalid(String.format(this.minText, this.minValue));
43171             return false;
43172         }
43173         
43174         if(num > this.maxValue){
43175             this.markInvalid(String.format(this.maxText, this.maxValue));
43176             return false;
43177         }
43178         
43179         return true;
43180     },
43181     
43182     validate : function()
43183     {
43184         if(this.disabled || this.allowBlank){
43185             this.markValid();
43186             return true;
43187         }
43188         
43189         var currency = this.getCurrency();
43190         
43191         if(this.validateValue(this.getRawValue()) && currency.length){
43192             this.markValid();
43193             return true;
43194         }
43195         
43196         this.markInvalid();
43197         return false;
43198     },
43199     
43200     getName: function()
43201     {
43202         return this.name;
43203     },
43204     
43205     beforeBlur : function()
43206     {
43207         if(!this.castInt){
43208             return;
43209         }
43210         
43211         var v = this.parseValue(this.getRawValue());
43212         
43213         if(v || v == 0){
43214             this.setValue(v);
43215         }
43216     },
43217     
43218     onBlur : function()
43219     {
43220         this.beforeBlur();
43221         
43222         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43223             //this.el.removeClass(this.focusClass);
43224         }
43225         
43226         this.hasFocus = false;
43227         
43228         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43229             this.validate();
43230         }
43231         
43232         var v = this.getValue();
43233         
43234         if(String(v) !== String(this.startValue)){
43235             this.fireEvent('change', this, v, this.startValue);
43236         }
43237         
43238         this.fireEvent("blur", this);
43239     },
43240     
43241     inputEl : function()
43242     {
43243         return this.el.select('.roo-money-amount-input', true).first();
43244     },
43245     
43246     currencyEl : function()
43247     {
43248         return this.el.select('.roo-money-currency-input', true).first();
43249     },
43250     
43251     hiddenEl : function()
43252     {
43253         return this.el.select('input.hidden-number-input',true).first();
43254     }
43255     
43256 });/**
43257  * @class Roo.bootstrap.BezierSignature
43258  * @extends Roo.bootstrap.Component
43259  * Bootstrap BezierSignature class
43260  * This script refer to:
43261  *    Title: Signature Pad
43262  *    Author: szimek
43263  *    Availability: https://github.com/szimek/signature_pad
43264  *
43265  * @constructor
43266  * Create a new BezierSignature
43267  * @param {Object} config The config object
43268  */
43269
43270 Roo.bootstrap.BezierSignature = function(config){
43271     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43272     this.addEvents({
43273         "resize" : true
43274     });
43275 };
43276
43277 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43278 {
43279      
43280     curve_data: [],
43281     
43282     is_empty: true,
43283     
43284     mouse_btn_down: true,
43285     
43286     /**
43287      * @cfg {int} canvas height
43288      */
43289     canvas_height: '200px',
43290     
43291     /**
43292      * @cfg {float|function} Radius of a single dot.
43293      */ 
43294     dot_size: false,
43295     
43296     /**
43297      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43298      */
43299     min_width: 0.5,
43300     
43301     /**
43302      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43303      */
43304     max_width: 2.5,
43305     
43306     /**
43307      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43308      */
43309     throttle: 16,
43310     
43311     /**
43312      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43313      */
43314     min_distance: 5,
43315     
43316     /**
43317      * @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.
43318      */
43319     bg_color: 'rgba(0, 0, 0, 0)',
43320     
43321     /**
43322      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43323      */
43324     dot_color: 'black',
43325     
43326     /**
43327      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43328      */ 
43329     velocity_filter_weight: 0.7,
43330     
43331     /**
43332      * @cfg {function} Callback when stroke begin. 
43333      */
43334     onBegin: false,
43335     
43336     /**
43337      * @cfg {function} Callback when stroke end.
43338      */
43339     onEnd: false,
43340     
43341     getAutoCreate : function()
43342     {
43343         var cls = 'roo-signature column';
43344         
43345         if(this.cls){
43346             cls += ' ' + this.cls;
43347         }
43348         
43349         var col_sizes = [
43350             'lg',
43351             'md',
43352             'sm',
43353             'xs'
43354         ];
43355         
43356         for(var i = 0; i < col_sizes.length; i++) {
43357             if(this[col_sizes[i]]) {
43358                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43359             }
43360         }
43361         
43362         var cfg = {
43363             tag: 'div',
43364             cls: cls,
43365             cn: [
43366                 {
43367                     tag: 'div',
43368                     cls: 'roo-signature-body',
43369                     cn: [
43370                         {
43371                             tag: 'canvas',
43372                             cls: 'roo-signature-body-canvas',
43373                             height: this.canvas_height,
43374                             width: this.canvas_width
43375                         }
43376                     ]
43377                 },
43378                 {
43379                     tag: 'input',
43380                     type: 'file',
43381                     style: 'display: none'
43382                 }
43383             ]
43384         };
43385         
43386         return cfg;
43387     },
43388     
43389     initEvents: function() 
43390     {
43391         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43392         
43393         var canvas = this.canvasEl();
43394         
43395         // mouse && touch event swapping...
43396         canvas.dom.style.touchAction = 'none';
43397         canvas.dom.style.msTouchAction = 'none';
43398         
43399         this.mouse_btn_down = false;
43400         canvas.on('mousedown', this._handleMouseDown, this);
43401         canvas.on('mousemove', this._handleMouseMove, this);
43402         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43403         
43404         if (window.PointerEvent) {
43405             canvas.on('pointerdown', this._handleMouseDown, this);
43406             canvas.on('pointermove', this._handleMouseMove, this);
43407             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43408         }
43409         
43410         if ('ontouchstart' in window) {
43411             canvas.on('touchstart', this._handleTouchStart, this);
43412             canvas.on('touchmove', this._handleTouchMove, this);
43413             canvas.on('touchend', this._handleTouchEnd, this);
43414         }
43415         
43416         Roo.EventManager.onWindowResize(this.resize, this, true);
43417         
43418         // file input event
43419         this.fileEl().on('change', this.uploadImage, this);
43420         
43421         this.clear();
43422         
43423         this.resize();
43424     },
43425     
43426     resize: function(){
43427         
43428         var canvas = this.canvasEl().dom;
43429         var ctx = this.canvasElCtx();
43430         var img_data = false;
43431         
43432         if(canvas.width > 0) {
43433             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43434         }
43435         // setting canvas width will clean img data
43436         canvas.width = 0;
43437         
43438         var style = window.getComputedStyle ? 
43439             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43440             
43441         var padding_left = parseInt(style.paddingLeft) || 0;
43442         var padding_right = parseInt(style.paddingRight) || 0;
43443         
43444         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43445         
43446         if(img_data) {
43447             ctx.putImageData(img_data, 0, 0);
43448         }
43449     },
43450     
43451     _handleMouseDown: function(e)
43452     {
43453         if (e.browserEvent.which === 1) {
43454             this.mouse_btn_down = true;
43455             this.strokeBegin(e);
43456         }
43457     },
43458     
43459     _handleMouseMove: function (e)
43460     {
43461         if (this.mouse_btn_down) {
43462             this.strokeMoveUpdate(e);
43463         }
43464     },
43465     
43466     _handleMouseUp: function (e)
43467     {
43468         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43469             this.mouse_btn_down = false;
43470             this.strokeEnd(e);
43471         }
43472     },
43473     
43474     _handleTouchStart: function (e) {
43475         
43476         e.preventDefault();
43477         if (e.browserEvent.targetTouches.length === 1) {
43478             // var touch = e.browserEvent.changedTouches[0];
43479             // this.strokeBegin(touch);
43480             
43481              this.strokeBegin(e); // assume e catching the correct xy...
43482         }
43483     },
43484     
43485     _handleTouchMove: function (e) {
43486         e.preventDefault();
43487         // var touch = event.targetTouches[0];
43488         // _this._strokeMoveUpdate(touch);
43489         this.strokeMoveUpdate(e);
43490     },
43491     
43492     _handleTouchEnd: function (e) {
43493         var wasCanvasTouched = e.target === this.canvasEl().dom;
43494         if (wasCanvasTouched) {
43495             e.preventDefault();
43496             // var touch = event.changedTouches[0];
43497             // _this._strokeEnd(touch);
43498             this.strokeEnd(e);
43499         }
43500     },
43501     
43502     reset: function () {
43503         this._lastPoints = [];
43504         this._lastVelocity = 0;
43505         this._lastWidth = (this.min_width + this.max_width) / 2;
43506         this.canvasElCtx().fillStyle = this.dot_color;
43507     },
43508     
43509     strokeMoveUpdate: function(e)
43510     {
43511         this.strokeUpdate(e);
43512         
43513         if (this.throttle) {
43514             this.throttleStroke(this.strokeUpdate, this.throttle);
43515         }
43516         else {
43517             this.strokeUpdate(e);
43518         }
43519     },
43520     
43521     strokeBegin: function(e)
43522     {
43523         var newPointGroup = {
43524             color: this.dot_color,
43525             points: []
43526         };
43527         
43528         if (typeof this.onBegin === 'function') {
43529             this.onBegin(e);
43530         }
43531         
43532         this.curve_data.push(newPointGroup);
43533         this.reset();
43534         this.strokeUpdate(e);
43535     },
43536     
43537     strokeUpdate: function(e)
43538     {
43539         var rect = this.canvasEl().dom.getBoundingClientRect();
43540         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43541         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43542         var lastPoints = lastPointGroup.points;
43543         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43544         var isLastPointTooClose = lastPoint
43545             ? point.distanceTo(lastPoint) <= this.min_distance
43546             : false;
43547         var color = lastPointGroup.color;
43548         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43549             var curve = this.addPoint(point);
43550             if (!lastPoint) {
43551                 this.drawDot({color: color, point: point});
43552             }
43553             else if (curve) {
43554                 this.drawCurve({color: color, curve: curve});
43555             }
43556             lastPoints.push({
43557                 time: point.time,
43558                 x: point.x,
43559                 y: point.y
43560             });
43561         }
43562     },
43563     
43564     strokeEnd: function(e)
43565     {
43566         this.strokeUpdate(e);
43567         if (typeof this.onEnd === 'function') {
43568             this.onEnd(e);
43569         }
43570     },
43571     
43572     addPoint:  function (point) {
43573         var _lastPoints = this._lastPoints;
43574         _lastPoints.push(point);
43575         if (_lastPoints.length > 2) {
43576             if (_lastPoints.length === 3) {
43577                 _lastPoints.unshift(_lastPoints[0]);
43578             }
43579             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43580             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43581             _lastPoints.shift();
43582             return curve;
43583         }
43584         return null;
43585     },
43586     
43587     calculateCurveWidths: function (startPoint, endPoint) {
43588         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43589             (1 - this.velocity_filter_weight) * this._lastVelocity;
43590
43591         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43592         var widths = {
43593             end: newWidth,
43594             start: this._lastWidth
43595         };
43596         
43597         this._lastVelocity = velocity;
43598         this._lastWidth = newWidth;
43599         return widths;
43600     },
43601     
43602     drawDot: function (_a) {
43603         var color = _a.color, point = _a.point;
43604         var ctx = this.canvasElCtx();
43605         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43606         ctx.beginPath();
43607         this.drawCurveSegment(point.x, point.y, width);
43608         ctx.closePath();
43609         ctx.fillStyle = color;
43610         ctx.fill();
43611     },
43612     
43613     drawCurve: function (_a) {
43614         var color = _a.color, curve = _a.curve;
43615         var ctx = this.canvasElCtx();
43616         var widthDelta = curve.endWidth - curve.startWidth;
43617         var drawSteps = Math.floor(curve.length()) * 2;
43618         ctx.beginPath();
43619         ctx.fillStyle = color;
43620         for (var i = 0; i < drawSteps; i += 1) {
43621         var t = i / drawSteps;
43622         var tt = t * t;
43623         var ttt = tt * t;
43624         var u = 1 - t;
43625         var uu = u * u;
43626         var uuu = uu * u;
43627         var x = uuu * curve.startPoint.x;
43628         x += 3 * uu * t * curve.control1.x;
43629         x += 3 * u * tt * curve.control2.x;
43630         x += ttt * curve.endPoint.x;
43631         var y = uuu * curve.startPoint.y;
43632         y += 3 * uu * t * curve.control1.y;
43633         y += 3 * u * tt * curve.control2.y;
43634         y += ttt * curve.endPoint.y;
43635         var width = curve.startWidth + ttt * widthDelta;
43636         this.drawCurveSegment(x, y, width);
43637         }
43638         ctx.closePath();
43639         ctx.fill();
43640     },
43641     
43642     drawCurveSegment: function (x, y, width) {
43643         var ctx = this.canvasElCtx();
43644         ctx.moveTo(x, y);
43645         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43646         this.is_empty = false;
43647     },
43648     
43649     clear: function()
43650     {
43651         var ctx = this.canvasElCtx();
43652         var canvas = this.canvasEl().dom;
43653         ctx.fillStyle = this.bg_color;
43654         ctx.clearRect(0, 0, canvas.width, canvas.height);
43655         ctx.fillRect(0, 0, canvas.width, canvas.height);
43656         this.curve_data = [];
43657         this.reset();
43658         this.is_empty = true;
43659     },
43660     
43661     fileEl: function()
43662     {
43663         return  this.el.select('input',true).first();
43664     },
43665     
43666     canvasEl: function()
43667     {
43668         return this.el.select('canvas',true).first();
43669     },
43670     
43671     canvasElCtx: function()
43672     {
43673         return this.el.select('canvas',true).first().dom.getContext('2d');
43674     },
43675     
43676     getImage: function(type)
43677     {
43678         if(this.is_empty) {
43679             return false;
43680         }
43681         
43682         // encryption ?
43683         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43684     },
43685     
43686     drawFromImage: function(img_src)
43687     {
43688         var img = new Image();
43689         
43690         img.onload = function(){
43691             this.canvasElCtx().drawImage(img, 0, 0);
43692         }.bind(this);
43693         
43694         img.src = img_src;
43695         
43696         this.is_empty = false;
43697     },
43698     
43699     selectImage: function()
43700     {
43701         this.fileEl().dom.click();
43702     },
43703     
43704     uploadImage: function(e)
43705     {
43706         var reader = new FileReader();
43707         
43708         reader.onload = function(e){
43709             var img = new Image();
43710             img.onload = function(){
43711                 this.reset();
43712                 this.canvasElCtx().drawImage(img, 0, 0);
43713             }.bind(this);
43714             img.src = e.target.result;
43715         }.bind(this);
43716         
43717         reader.readAsDataURL(e.target.files[0]);
43718     },
43719     
43720     // Bezier Point Constructor
43721     Point: (function () {
43722         function Point(x, y, time) {
43723             this.x = x;
43724             this.y = y;
43725             this.time = time || Date.now();
43726         }
43727         Point.prototype.distanceTo = function (start) {
43728             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43729         };
43730         Point.prototype.equals = function (other) {
43731             return this.x === other.x && this.y === other.y && this.time === other.time;
43732         };
43733         Point.prototype.velocityFrom = function (start) {
43734             return this.time !== start.time
43735             ? this.distanceTo(start) / (this.time - start.time)
43736             : 0;
43737         };
43738         return Point;
43739     }()),
43740     
43741     
43742     // Bezier Constructor
43743     Bezier: (function () {
43744         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43745             this.startPoint = startPoint;
43746             this.control2 = control2;
43747             this.control1 = control1;
43748             this.endPoint = endPoint;
43749             this.startWidth = startWidth;
43750             this.endWidth = endWidth;
43751         }
43752         Bezier.fromPoints = function (points, widths, scope) {
43753             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43754             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43755             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43756         };
43757         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43758             var dx1 = s1.x - s2.x;
43759             var dy1 = s1.y - s2.y;
43760             var dx2 = s2.x - s3.x;
43761             var dy2 = s2.y - s3.y;
43762             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43763             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43764             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43765             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43766             var dxm = m1.x - m2.x;
43767             var dym = m1.y - m2.y;
43768             var k = l2 / (l1 + l2);
43769             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43770             var tx = s2.x - cm.x;
43771             var ty = s2.y - cm.y;
43772             return {
43773                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43774                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43775             };
43776         };
43777         Bezier.prototype.length = function () {
43778             var steps = 10;
43779             var length = 0;
43780             var px;
43781             var py;
43782             for (var i = 0; i <= steps; i += 1) {
43783                 var t = i / steps;
43784                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43785                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43786                 if (i > 0) {
43787                     var xdiff = cx - px;
43788                     var ydiff = cy - py;
43789                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43790                 }
43791                 px = cx;
43792                 py = cy;
43793             }
43794             return length;
43795         };
43796         Bezier.prototype.point = function (t, start, c1, c2, end) {
43797             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43798             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43799             + (3.0 * c2 * (1.0 - t) * t * t)
43800             + (end * t * t * t);
43801         };
43802         return Bezier;
43803     }()),
43804     
43805     throttleStroke: function(fn, wait) {
43806       if (wait === void 0) { wait = 250; }
43807       var previous = 0;
43808       var timeout = null;
43809       var result;
43810       var storedContext;
43811       var storedArgs;
43812       var later = function () {
43813           previous = Date.now();
43814           timeout = null;
43815           result = fn.apply(storedContext, storedArgs);
43816           if (!timeout) {
43817               storedContext = null;
43818               storedArgs = [];
43819           }
43820       };
43821       return function wrapper() {
43822           var args = [];
43823           for (var _i = 0; _i < arguments.length; _i++) {
43824               args[_i] = arguments[_i];
43825           }
43826           var now = Date.now();
43827           var remaining = wait - (now - previous);
43828           storedContext = this;
43829           storedArgs = args;
43830           if (remaining <= 0 || remaining > wait) {
43831               if (timeout) {
43832                   clearTimeout(timeout);
43833                   timeout = null;
43834               }
43835               previous = now;
43836               result = fn.apply(storedContext, storedArgs);
43837               if (!timeout) {
43838                   storedContext = null;
43839                   storedArgs = [];
43840               }
43841           }
43842           else if (!timeout) {
43843               timeout = window.setTimeout(later, remaining);
43844           }
43845           return result;
43846       };
43847   }
43848   
43849 });
43850
43851  
43852
43853