roojs-ui.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  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <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>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.header = html;
2683         if (this.headerContainerEl) {
2684             this.headerContainerEl.dom.innerHTML = html;
2685         }
2686     }
2687
2688     
2689 });
2690
2691 /*
2692  * - LGPL
2693  *
2694  * Card header - holder for the card header elements.
2695  * 
2696  */
2697
2698 /**
2699  * @class Roo.bootstrap.CardHeader
2700  * @extends Roo.bootstrap.Element
2701  * Bootstrap CardHeader class
2702  * @constructor
2703  * Create a new Card Header - that you can embed children into
2704  * @param {Object} config The config object
2705  */
2706
2707 Roo.bootstrap.CardHeader = function(config){
2708     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2709 };
2710
2711 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2712     
2713     
2714     container_method : 'getCardHeader' 
2715     
2716      
2717     
2718     
2719    
2720 });
2721
2722  
2723
2724  /*
2725  * - LGPL
2726  *
2727  * Card footer - holder for the card footer elements.
2728  * 
2729  */
2730
2731 /**
2732  * @class Roo.bootstrap.CardFooter
2733  * @extends Roo.bootstrap.Element
2734  * Bootstrap CardFooter class
2735  * @constructor
2736  * Create a new Card Footer - that you can embed children into
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.CardFooter = function(config){
2741     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2745     
2746     
2747     container_method : 'getCardFooter' 
2748     
2749      
2750     
2751     
2752    
2753 });
2754
2755  
2756
2757  /*
2758  * - LGPL
2759  *
2760  * Card header - holder for the card header elements.
2761  * 
2762  */
2763
2764 /**
2765  * @class Roo.bootstrap.CardImageTop
2766  * @extends Roo.bootstrap.Element
2767  * Bootstrap CardImageTop class
2768  * @constructor
2769  * Create a new Card Image Top container
2770  * @param {Object} config The config object
2771  */
2772
2773 Roo.bootstrap.CardImageTop = function(config){
2774     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2775 };
2776
2777 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2778     
2779    
2780     container_method : 'getCardImageTop' 
2781     
2782      
2783     
2784    
2785 });
2786
2787  
2788
2789  /*
2790  * - LGPL
2791  *
2792  * image
2793  * 
2794  */
2795
2796
2797 /**
2798  * @class Roo.bootstrap.Img
2799  * @extends Roo.bootstrap.Component
2800  * Bootstrap Img class
2801  * @cfg {Boolean} imgResponsive false | true
2802  * @cfg {String} border rounded | circle | thumbnail
2803  * @cfg {String} src image source
2804  * @cfg {String} alt image alternative text
2805  * @cfg {String} href a tag href
2806  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2807  * @cfg {String} xsUrl xs image source
2808  * @cfg {String} smUrl sm image source
2809  * @cfg {String} mdUrl md image source
2810  * @cfg {String} lgUrl lg image source
2811  * 
2812  * @constructor
2813  * Create a new Input
2814  * @param {Object} config The config object
2815  */
2816
2817 Roo.bootstrap.Img = function(config){
2818     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2819     
2820     this.addEvents({
2821         // img events
2822         /**
2823          * @event click
2824          * The img click event for the img.
2825          * @param {Roo.EventObject} e
2826          */
2827         "click" : true
2828     });
2829 };
2830
2831 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2832     
2833     imgResponsive: true,
2834     border: '',
2835     src: 'about:blank',
2836     href: false,
2837     target: false,
2838     xsUrl: '',
2839     smUrl: '',
2840     mdUrl: '',
2841     lgUrl: '',
2842
2843     getAutoCreate : function()
2844     {   
2845         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2846             return this.createSingleImg();
2847         }
2848         
2849         var cfg = {
2850             tag: 'div',
2851             cls: 'roo-image-responsive-group',
2852             cn: []
2853         };
2854         var _this = this;
2855         
2856         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2857             
2858             if(!_this[size + 'Url']){
2859                 return;
2860             }
2861             
2862             var img = {
2863                 tag: 'img',
2864                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2865                 html: _this.html || cfg.html,
2866                 src: _this[size + 'Url']
2867             };
2868             
2869             img.cls += ' roo-image-responsive-' + size;
2870             
2871             var s = ['xs', 'sm', 'md', 'lg'];
2872             
2873             s.splice(s.indexOf(size), 1);
2874             
2875             Roo.each(s, function(ss){
2876                 img.cls += ' hidden-' + ss;
2877             });
2878             
2879             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2880                 cfg.cls += ' img-' + _this.border;
2881             }
2882             
2883             if(_this.alt){
2884                 cfg.alt = _this.alt;
2885             }
2886             
2887             if(_this.href){
2888                 var a = {
2889                     tag: 'a',
2890                     href: _this.href,
2891                     cn: [
2892                         img
2893                     ]
2894                 };
2895
2896                 if(this.target){
2897                     a.target = _this.target;
2898                 }
2899             }
2900             
2901             cfg.cn.push((_this.href) ? a : img);
2902             
2903         });
2904         
2905         return cfg;
2906     },
2907     
2908     createSingleImg : function()
2909     {
2910         var cfg = {
2911             tag: 'img',
2912             cls: (this.imgResponsive) ? 'img-responsive' : '',
2913             html : null,
2914             src : 'about:blank'  // just incase src get's set to undefined?!?
2915         };
2916         
2917         cfg.html = this.html || cfg.html;
2918         
2919         cfg.src = this.src || cfg.src;
2920         
2921         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2922             cfg.cls += ' img-' + this.border;
2923         }
2924         
2925         if(this.alt){
2926             cfg.alt = this.alt;
2927         }
2928         
2929         if(this.href){
2930             var a = {
2931                 tag: 'a',
2932                 href: this.href,
2933                 cn: [
2934                     cfg
2935                 ]
2936             };
2937             
2938             if(this.target){
2939                 a.target = this.target;
2940             }
2941             
2942         }
2943         
2944         return (this.href) ? a : cfg;
2945     },
2946     
2947     initEvents: function() 
2948     {
2949         if(!this.href){
2950             this.el.on('click', this.onClick, this);
2951         }
2952         
2953     },
2954     
2955     onClick : function(e)
2956     {
2957         Roo.log('img onclick');
2958         this.fireEvent('click', this, e);
2959     },
2960     /**
2961      * Sets the url of the image - used to update it
2962      * @param {String} url the url of the image
2963      */
2964     
2965     setSrc : function(url)
2966     {
2967         this.src =  url;
2968         
2969         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2970             this.el.dom.src =  url;
2971             return;
2972         }
2973         
2974         this.el.select('img', true).first().dom.src =  url;
2975     }
2976     
2977     
2978    
2979 });
2980
2981  /*
2982  * - LGPL
2983  *
2984  * image
2985  * 
2986  */
2987
2988
2989 /**
2990  * @class Roo.bootstrap.Link
2991  * @extends Roo.bootstrap.Component
2992  * Bootstrap Link Class
2993  * @cfg {String} alt image alternative text
2994  * @cfg {String} href a tag href
2995  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2996  * @cfg {String} html the content of the link.
2997  * @cfg {String} anchor name for the anchor link
2998  * @cfg {String} fa - favicon
2999
3000  * @cfg {Boolean} preventDefault (true | false) default false
3001
3002  * 
3003  * @constructor
3004  * Create a new Input
3005  * @param {Object} config The config object
3006  */
3007
3008 Roo.bootstrap.Link = function(config){
3009     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3010     
3011     this.addEvents({
3012         // img events
3013         /**
3014          * @event click
3015          * The img click event for the img.
3016          * @param {Roo.EventObject} e
3017          */
3018         "click" : true
3019     });
3020 };
3021
3022 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3023     
3024     href: false,
3025     target: false,
3026     preventDefault: false,
3027     anchor : false,
3028     alt : false,
3029     fa: false,
3030
3031
3032     getAutoCreate : function()
3033     {
3034         var html = this.html || '';
3035         
3036         if (this.fa !== false) {
3037             html = '<i class="fa fa-' + this.fa + '"></i>';
3038         }
3039         var cfg = {
3040             tag: 'a'
3041         };
3042         // anchor's do not require html/href...
3043         if (this.anchor === false) {
3044             cfg.html = html;
3045             cfg.href = this.href || '#';
3046         } else {
3047             cfg.name = this.anchor;
3048             if (this.html !== false || this.fa !== false) {
3049                 cfg.html = html;
3050             }
3051             if (this.href !== false) {
3052                 cfg.href = this.href;
3053             }
3054         }
3055         
3056         if(this.alt !== false){
3057             cfg.alt = this.alt;
3058         }
3059         
3060         
3061         if(this.target !== false) {
3062             cfg.target = this.target;
3063         }
3064         
3065         return cfg;
3066     },
3067     
3068     initEvents: function() {
3069         
3070         if(!this.href || this.preventDefault){
3071             this.el.on('click', this.onClick, this);
3072         }
3073     },
3074     
3075     onClick : function(e)
3076     {
3077         if(this.preventDefault){
3078             e.preventDefault();
3079         }
3080         //Roo.log('img onclick');
3081         this.fireEvent('click', this, e);
3082     }
3083    
3084 });
3085
3086  /*
3087  * - LGPL
3088  *
3089  * header
3090  * 
3091  */
3092
3093 /**
3094  * @class Roo.bootstrap.Header
3095  * @extends Roo.bootstrap.Component
3096  * Bootstrap Header class
3097  * @cfg {String} html content of header
3098  * @cfg {Number} level (1|2|3|4|5|6) default 1
3099  * 
3100  * @constructor
3101  * Create a new Header
3102  * @param {Object} config The config object
3103  */
3104
3105
3106 Roo.bootstrap.Header  = function(config){
3107     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3108 };
3109
3110 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3111     
3112     //href : false,
3113     html : false,
3114     level : 1,
3115     
3116     
3117     
3118     getAutoCreate : function(){
3119         
3120         
3121         
3122         var cfg = {
3123             tag: 'h' + (1 *this.level),
3124             html: this.html || ''
3125         } ;
3126         
3127         return cfg;
3128     }
3129    
3130 });
3131
3132  
3133
3134  /*
3135  * Based on:
3136  * Ext JS Library 1.1.1
3137  * Copyright(c) 2006-2007, Ext JS, LLC.
3138  *
3139  * Originally Released Under LGPL - original licence link has changed is not relivant.
3140  *
3141  * Fork - LGPL
3142  * <script type="text/javascript">
3143  */
3144  
3145 /**
3146  * @class Roo.bootstrap.MenuMgr
3147  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3148  * @singleton
3149  */
3150 Roo.bootstrap.MenuMgr = function(){
3151    var menus, active, groups = {}, attached = false, lastShow = new Date();
3152
3153    // private - called when first menu is created
3154    function init(){
3155        menus = {};
3156        active = new Roo.util.MixedCollection();
3157        Roo.get(document).addKeyListener(27, function(){
3158            if(active.length > 0){
3159                hideAll();
3160            }
3161        });
3162    }
3163
3164    // private
3165    function hideAll(){
3166        if(active && active.length > 0){
3167            var c = active.clone();
3168            c.each(function(m){
3169                m.hide();
3170            });
3171        }
3172    }
3173
3174    // private
3175    function onHide(m){
3176        active.remove(m);
3177        if(active.length < 1){
3178            Roo.get(document).un("mouseup", onMouseDown);
3179             
3180            attached = false;
3181        }
3182    }
3183
3184    // private
3185    function onShow(m){
3186        var last = active.last();
3187        lastShow = new Date();
3188        active.add(m);
3189        if(!attached){
3190           Roo.get(document).on("mouseup", onMouseDown);
3191            
3192            attached = true;
3193        }
3194        if(m.parentMenu){
3195           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3196           m.parentMenu.activeChild = m;
3197        }else if(last && last.isVisible()){
3198           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3199        }
3200    }
3201
3202    // private
3203    function onBeforeHide(m){
3204        if(m.activeChild){
3205            m.activeChild.hide();
3206        }
3207        if(m.autoHideTimer){
3208            clearTimeout(m.autoHideTimer);
3209            delete m.autoHideTimer;
3210        }
3211    }
3212
3213    // private
3214    function onBeforeShow(m){
3215        var pm = m.parentMenu;
3216        if(!pm && !m.allowOtherMenus){
3217            hideAll();
3218        }else if(pm && pm.activeChild && active != m){
3219            pm.activeChild.hide();
3220        }
3221    }
3222
3223    // private this should really trigger on mouseup..
3224    function onMouseDown(e){
3225         Roo.log("on Mouse Up");
3226         
3227         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3228             Roo.log("MenuManager hideAll");
3229             hideAll();
3230             e.stopEvent();
3231         }
3232         
3233         
3234    }
3235
3236    // private
3237    function onBeforeCheck(mi, state){
3238        if(state){
3239            var g = groups[mi.group];
3240            for(var i = 0, l = g.length; i < l; i++){
3241                if(g[i] != mi){
3242                    g[i].setChecked(false);
3243                }
3244            }
3245        }
3246    }
3247
3248    return {
3249
3250        /**
3251         * Hides all menus that are currently visible
3252         */
3253        hideAll : function(){
3254             hideAll();  
3255        },
3256
3257        // private
3258        register : function(menu){
3259            if(!menus){
3260                init();
3261            }
3262            menus[menu.id] = menu;
3263            menu.on("beforehide", onBeforeHide);
3264            menu.on("hide", onHide);
3265            menu.on("beforeshow", onBeforeShow);
3266            menu.on("show", onShow);
3267            var g = menu.group;
3268            if(g && menu.events["checkchange"]){
3269                if(!groups[g]){
3270                    groups[g] = [];
3271                }
3272                groups[g].push(menu);
3273                menu.on("checkchange", onCheck);
3274            }
3275        },
3276
3277         /**
3278          * Returns a {@link Roo.menu.Menu} object
3279          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3280          * be used to generate and return a new Menu instance.
3281          */
3282        get : function(menu){
3283            if(typeof menu == "string"){ // menu id
3284                return menus[menu];
3285            }else if(menu.events){  // menu instance
3286                return menu;
3287            }
3288            /*else if(typeof menu.length == 'number'){ // array of menu items?
3289                return new Roo.bootstrap.Menu({items:menu});
3290            }else{ // otherwise, must be a config
3291                return new Roo.bootstrap.Menu(menu);
3292            }
3293            */
3294            return false;
3295        },
3296
3297        // private
3298        unregister : function(menu){
3299            delete menus[menu.id];
3300            menu.un("beforehide", onBeforeHide);
3301            menu.un("hide", onHide);
3302            menu.un("beforeshow", onBeforeShow);
3303            menu.un("show", onShow);
3304            var g = menu.group;
3305            if(g && menu.events["checkchange"]){
3306                groups[g].remove(menu);
3307                menu.un("checkchange", onCheck);
3308            }
3309        },
3310
3311        // private
3312        registerCheckable : function(menuItem){
3313            var g = menuItem.group;
3314            if(g){
3315                if(!groups[g]){
3316                    groups[g] = [];
3317                }
3318                groups[g].push(menuItem);
3319                menuItem.on("beforecheckchange", onBeforeCheck);
3320            }
3321        },
3322
3323        // private
3324        unregisterCheckable : function(menuItem){
3325            var g = menuItem.group;
3326            if(g){
3327                groups[g].remove(menuItem);
3328                menuItem.un("beforecheckchange", onBeforeCheck);
3329            }
3330        }
3331    };
3332 }();/*
3333  * - LGPL
3334  *
3335  * menu
3336  * 
3337  */
3338
3339 /**
3340  * @class Roo.bootstrap.Menu
3341  * @extends Roo.bootstrap.Component
3342  * Bootstrap Menu class - container for MenuItems
3343  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3344  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3345  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3346  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3347  * 
3348  * @constructor
3349  * Create a new Menu
3350  * @param {Object} config The config object
3351  */
3352
3353
3354 Roo.bootstrap.Menu = function(config){
3355     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3356     if (this.registerMenu && this.type != 'treeview')  {
3357         Roo.bootstrap.MenuMgr.register(this);
3358     }
3359     
3360     
3361     this.addEvents({
3362         /**
3363          * @event beforeshow
3364          * Fires before this menu is displayed (return false to block)
3365          * @param {Roo.menu.Menu} this
3366          */
3367         beforeshow : true,
3368         /**
3369          * @event beforehide
3370          * Fires before this menu is hidden (return false to block)
3371          * @param {Roo.menu.Menu} this
3372          */
3373         beforehide : true,
3374         /**
3375          * @event show
3376          * Fires after this menu is displayed
3377          * @param {Roo.menu.Menu} this
3378          */
3379         show : true,
3380         /**
3381          * @event hide
3382          * Fires after this menu is hidden
3383          * @param {Roo.menu.Menu} this
3384          */
3385         hide : true,
3386         /**
3387          * @event click
3388          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3389          * @param {Roo.menu.Menu} this
3390          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3391          * @param {Roo.EventObject} e
3392          */
3393         click : true,
3394         /**
3395          * @event mouseover
3396          * Fires when the mouse is hovering over this menu
3397          * @param {Roo.menu.Menu} this
3398          * @param {Roo.EventObject} e
3399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3400          */
3401         mouseover : true,
3402         /**
3403          * @event mouseout
3404          * Fires when the mouse exits this menu
3405          * @param {Roo.menu.Menu} this
3406          * @param {Roo.EventObject} e
3407          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3408          */
3409         mouseout : true,
3410         /**
3411          * @event itemclick
3412          * Fires when a menu item contained in this menu is clicked
3413          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3414          * @param {Roo.EventObject} e
3415          */
3416         itemclick: true
3417     });
3418     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3419 };
3420
3421 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3422     
3423    /// html : false,
3424     //align : '',
3425     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3426     type: false,
3427     /**
3428      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3429      */
3430     registerMenu : true,
3431     
3432     menuItems :false, // stores the menu items..
3433     
3434     hidden:true,
3435         
3436     parentMenu : false,
3437     
3438     stopEvent : true,
3439     
3440     isLink : false,
3441     
3442     getChildContainer : function() {
3443         return this.el;  
3444     },
3445     
3446     getAutoCreate : function(){
3447          
3448         //if (['right'].indexOf(this.align)!==-1) {
3449         //    cfg.cn[1].cls += ' pull-right'
3450         //}
3451         
3452         
3453         var cfg = {
3454             tag : 'ul',
3455             cls : 'dropdown-menu' ,
3456             style : 'z-index:1000'
3457             
3458         };
3459         
3460         if (this.type === 'submenu') {
3461             cfg.cls = 'submenu active';
3462         }
3463         if (this.type === 'treeview') {
3464             cfg.cls = 'treeview-menu';
3465         }
3466         
3467         return cfg;
3468     },
3469     initEvents : function() {
3470         
3471        // Roo.log("ADD event");
3472        // Roo.log(this.triggerEl.dom);
3473         
3474         this.triggerEl.on('click', this.onTriggerClick, this);
3475         
3476         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3477         
3478         
3479         if (this.triggerEl.hasClass('nav-item')) {
3480             // dropdown toggle on the 'a' in BS4?
3481             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3482         } else {
3483             this.triggerEl.addClass('dropdown-toggle');
3484         }
3485         if (Roo.isTouch) {
3486             this.el.on('touchstart'  , this.onTouch, this);
3487         }
3488         this.el.on('click' , this.onClick, this);
3489
3490         this.el.on("mouseover", this.onMouseOver, this);
3491         this.el.on("mouseout", this.onMouseOut, this);
3492         
3493     },
3494     
3495     findTargetItem : function(e)
3496     {
3497         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3498         if(!t){
3499             return false;
3500         }
3501         //Roo.log(t);         Roo.log(t.id);
3502         if(t && t.id){
3503             //Roo.log(this.menuitems);
3504             return this.menuitems.get(t.id);
3505             
3506             //return this.items.get(t.menuItemId);
3507         }
3508         
3509         return false;
3510     },
3511     
3512     onTouch : function(e) 
3513     {
3514         Roo.log("menu.onTouch");
3515         //e.stopEvent(); this make the user popdown broken
3516         this.onClick(e);
3517     },
3518     
3519     onClick : function(e)
3520     {
3521         Roo.log("menu.onClick");
3522         
3523         var t = this.findTargetItem(e);
3524         if(!t || t.isContainer){
3525             return;
3526         }
3527         Roo.log(e);
3528         /*
3529         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3530             if(t == this.activeItem && t.shouldDeactivate(e)){
3531                 this.activeItem.deactivate();
3532                 delete this.activeItem;
3533                 return;
3534             }
3535             if(t.canActivate){
3536                 this.setActiveItem(t, true);
3537             }
3538             return;
3539             
3540             
3541         }
3542         */
3543        
3544         Roo.log('pass click event');
3545         
3546         t.onClick(e);
3547         
3548         this.fireEvent("click", this, t, e);
3549         
3550         var _this = this;
3551         
3552         if(!t.href.length || t.href == '#'){
3553             (function() { _this.hide(); }).defer(100);
3554         }
3555         
3556     },
3557     
3558     onMouseOver : function(e){
3559         var t  = this.findTargetItem(e);
3560         //Roo.log(t);
3561         //if(t){
3562         //    if(t.canActivate && !t.disabled){
3563         //        this.setActiveItem(t, true);
3564         //    }
3565         //}
3566         
3567         this.fireEvent("mouseover", this, e, t);
3568     },
3569     isVisible : function(){
3570         return !this.hidden;
3571     },
3572     onMouseOut : function(e){
3573         var t  = this.findTargetItem(e);
3574         
3575         //if(t ){
3576         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3577         //        this.activeItem.deactivate();
3578         //        delete this.activeItem;
3579         //    }
3580         //}
3581         this.fireEvent("mouseout", this, e, t);
3582     },
3583     
3584     
3585     /**
3586      * Displays this menu relative to another element
3587      * @param {String/HTMLElement/Roo.Element} element The element to align to
3588      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3589      * the element (defaults to this.defaultAlign)
3590      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3591      */
3592     show : function(el, pos, parentMenu)
3593     {
3594         if (false === this.fireEvent("beforeshow", this)) {
3595             Roo.log("show canceled");
3596             return;
3597         }
3598         this.parentMenu = parentMenu;
3599         if(!this.el){
3600             this.render();
3601         }
3602         
3603         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3604     },
3605      /**
3606      * Displays this menu at a specific xy position
3607      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3608      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3609      */
3610     showAt : function(xy, parentMenu, /* private: */_e){
3611         this.parentMenu = parentMenu;
3612         if(!this.el){
3613             this.render();
3614         }
3615         if(_e !== false){
3616             this.fireEvent("beforeshow", this);
3617             //xy = this.el.adjustForConstraints(xy);
3618         }
3619         
3620         //this.el.show();
3621         this.hideMenuItems();
3622         this.hidden = false;
3623         this.triggerEl.addClass('open');
3624         this.el.addClass('show');
3625         
3626         // reassign x when hitting right
3627         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3628             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3629         }
3630         
3631         // reassign y when hitting bottom
3632         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3633             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3634         }
3635         
3636         // but the list may align on trigger left or trigger top... should it be a properity?
3637         
3638         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3639             this.el.setXY(xy);
3640         }
3641         
3642         this.focus();
3643         this.fireEvent("show", this);
3644     },
3645     
3646     focus : function(){
3647         return;
3648         if(!this.hidden){
3649             this.doFocus.defer(50, this);
3650         }
3651     },
3652
3653     doFocus : function(){
3654         if(!this.hidden){
3655             this.focusEl.focus();
3656         }
3657     },
3658
3659     /**
3660      * Hides this menu and optionally all parent menus
3661      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3662      */
3663     hide : function(deep)
3664     {
3665         if (false === this.fireEvent("beforehide", this)) {
3666             Roo.log("hide canceled");
3667             return;
3668         }
3669         this.hideMenuItems();
3670         if(this.el && this.isVisible()){
3671            
3672             if(this.activeItem){
3673                 this.activeItem.deactivate();
3674                 this.activeItem = null;
3675             }
3676             this.triggerEl.removeClass('open');;
3677             this.el.removeClass('show');
3678             this.hidden = true;
3679             this.fireEvent("hide", this);
3680         }
3681         if(deep === true && this.parentMenu){
3682             this.parentMenu.hide(true);
3683         }
3684     },
3685     
3686     onTriggerClick : function(e)
3687     {
3688         Roo.log('trigger click');
3689         
3690         var target = e.getTarget();
3691         
3692         Roo.log(target.nodeName.toLowerCase());
3693         
3694         if(target.nodeName.toLowerCase() === 'i'){
3695             e.preventDefault();
3696         }
3697         
3698     },
3699     
3700     onTriggerPress  : function(e)
3701     {
3702         Roo.log('trigger press');
3703         //Roo.log(e.getTarget());
3704        // Roo.log(this.triggerEl.dom);
3705        
3706         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3707         var pel = Roo.get(e.getTarget());
3708         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3709             Roo.log('is treeview or dropdown?');
3710             return;
3711         }
3712         
3713         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3714             return;
3715         }
3716         
3717         if (this.isVisible()) {
3718             Roo.log('hide');
3719             this.hide();
3720         } else {
3721             Roo.log('show');
3722             this.show(this.triggerEl, '?', false);
3723         }
3724         
3725         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3726             e.stopEvent();
3727         }
3728         
3729     },
3730        
3731     
3732     hideMenuItems : function()
3733     {
3734         Roo.log("hide Menu Items");
3735         if (!this.el) { 
3736             return;
3737         }
3738         
3739         this.el.select('.open',true).each(function(aa) {
3740             
3741             aa.removeClass('open');
3742          
3743         });
3744     },
3745     addxtypeChild : function (tree, cntr) {
3746         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3747           
3748         this.menuitems.add(comp);
3749         return comp;
3750
3751     },
3752     getEl : function()
3753     {
3754         Roo.log(this.el);
3755         return this.el;
3756     },
3757     
3758     clear : function()
3759     {
3760         this.getEl().dom.innerHTML = '';
3761         this.menuitems.clear();
3762     }
3763 });
3764
3765  
3766  /*
3767  * - LGPL
3768  *
3769  * menu item
3770  * 
3771  */
3772
3773
3774 /**
3775  * @class Roo.bootstrap.MenuItem
3776  * @extends Roo.bootstrap.Component
3777  * Bootstrap MenuItem class
3778  * @cfg {String} html the menu label
3779  * @cfg {String} href the link
3780  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3781  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3782  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3783  * @cfg {String} fa favicon to show on left of menu item.
3784  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3785  * 
3786  * 
3787  * @constructor
3788  * Create a new MenuItem
3789  * @param {Object} config The config object
3790  */
3791
3792
3793 Roo.bootstrap.MenuItem = function(config){
3794     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3795     this.addEvents({
3796         // raw events
3797         /**
3798          * @event click
3799          * The raw click event for the entire grid.
3800          * @param {Roo.bootstrap.MenuItem} this
3801          * @param {Roo.EventObject} e
3802          */
3803         "click" : true
3804     });
3805 };
3806
3807 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3808     
3809     href : false,
3810     html : false,
3811     preventDefault: false,
3812     isContainer : false,
3813     active : false,
3814     fa: false,
3815     
3816     getAutoCreate : function(){
3817         
3818         if(this.isContainer){
3819             return {
3820                 tag: 'li',
3821                 cls: 'dropdown-menu-item '
3822             };
3823         }
3824         var ctag = {
3825             tag: 'span',
3826             html: 'Link'
3827         };
3828         
3829         var anc = {
3830             tag : 'a',
3831             cls : 'dropdown-item',
3832             href : '#',
3833             cn : [  ]
3834         };
3835         
3836         if (this.fa !== false) {
3837             anc.cn.push({
3838                 tag : 'i',
3839                 cls : 'fa fa-' + this.fa
3840             });
3841         }
3842         
3843         anc.cn.push(ctag);
3844         
3845         
3846         var cfg= {
3847             tag: 'li',
3848             cls: 'dropdown-menu-item',
3849             cn: [ anc ]
3850         };
3851         if (this.parent().type == 'treeview') {
3852             cfg.cls = 'treeview-menu';
3853         }
3854         if (this.active) {
3855             cfg.cls += ' active';
3856         }
3857         
3858         
3859         
3860         anc.href = this.href || cfg.cn[0].href ;
3861         ctag.html = this.html || cfg.cn[0].html ;
3862         return cfg;
3863     },
3864     
3865     initEvents: function()
3866     {
3867         if (this.parent().type == 'treeview') {
3868             this.el.select('a').on('click', this.onClick, this);
3869         }
3870         
3871         if (this.menu) {
3872             this.menu.parentType = this.xtype;
3873             this.menu.triggerEl = this.el;
3874             this.menu = this.addxtype(Roo.apply({}, this.menu));
3875         }
3876         
3877     },
3878     onClick : function(e)
3879     {
3880         Roo.log('item on click ');
3881         
3882         if(this.preventDefault){
3883             e.preventDefault();
3884         }
3885         //this.parent().hideMenuItems();
3886         
3887         this.fireEvent('click', this, e);
3888     },
3889     getEl : function()
3890     {
3891         return this.el;
3892     } 
3893 });
3894
3895  
3896
3897  /*
3898  * - LGPL
3899  *
3900  * menu separator
3901  * 
3902  */
3903
3904
3905 /**
3906  * @class Roo.bootstrap.MenuSeparator
3907  * @extends Roo.bootstrap.Component
3908  * Bootstrap MenuSeparator class
3909  * 
3910  * @constructor
3911  * Create a new MenuItem
3912  * @param {Object} config The config object
3913  */
3914
3915
3916 Roo.bootstrap.MenuSeparator = function(config){
3917     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3918 };
3919
3920 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3921     
3922     getAutoCreate : function(){
3923         var cfg = {
3924             cls: 'divider',
3925             tag : 'li'
3926         };
3927         
3928         return cfg;
3929     }
3930    
3931 });
3932
3933  
3934
3935  
3936 /*
3937 * Licence: LGPL
3938 */
3939
3940 /**
3941  * @class Roo.bootstrap.Modal
3942  * @extends Roo.bootstrap.Component
3943  * Bootstrap Modal class
3944  * @cfg {String} title Title of dialog
3945  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3946  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3947  * @cfg {Boolean} specificTitle default false
3948  * @cfg {Array} buttons Array of buttons or standard button set..
3949  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3950  * @cfg {Boolean} animate default true
3951  * @cfg {Boolean} allow_close default true
3952  * @cfg {Boolean} fitwindow default false
3953  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3954  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3955  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3956  * @cfg {String} size (sm|lg|xl) default empty
3957  * @cfg {Number} max_width set the max width of modal
3958  * @cfg {Boolean} editableTitle can the title be edited
3959
3960  *
3961  *
3962  * @constructor
3963  * Create a new Modal Dialog
3964  * @param {Object} config The config object
3965  */
3966
3967 Roo.bootstrap.Modal = function(config){
3968     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3969     this.addEvents({
3970         // raw events
3971         /**
3972          * @event btnclick
3973          * The raw btnclick event for the button
3974          * @param {Roo.EventObject} e
3975          */
3976         "btnclick" : true,
3977         /**
3978          * @event resize
3979          * Fire when dialog resize
3980          * @param {Roo.bootstrap.Modal} this
3981          * @param {Roo.EventObject} e
3982          */
3983         "resize" : true,
3984         /**
3985          * @event titlechanged
3986          * Fire when the editable title has been changed
3987          * @param {Roo.bootstrap.Modal} this
3988          * @param {Roo.EventObject} value
3989          */
3990         "titlechanged" : true 
3991         
3992     });
3993     this.buttons = this.buttons || [];
3994
3995     if (this.tmpl) {
3996         this.tmpl = Roo.factory(this.tmpl);
3997     }
3998
3999 };
4000
4001 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4002
4003     title : 'test dialog',
4004
4005     buttons : false,
4006
4007     // set on load...
4008
4009     html: false,
4010
4011     tmp: false,
4012
4013     specificTitle: false,
4014
4015     buttonPosition: 'right',
4016
4017     allow_close : true,
4018
4019     animate : true,
4020
4021     fitwindow: false,
4022     
4023      // private
4024     dialogEl: false,
4025     bodyEl:  false,
4026     footerEl:  false,
4027     titleEl:  false,
4028     closeEl:  false,
4029
4030     size: '',
4031     
4032     max_width: 0,
4033     
4034     max_height: 0,
4035     
4036     fit_content: false,
4037     editableTitle  : false,
4038
4039     onRender : function(ct, position)
4040     {
4041         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4042
4043         if(!this.el){
4044             var cfg = Roo.apply({},  this.getAutoCreate());
4045             cfg.id = Roo.id();
4046             //if(!cfg.name){
4047             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4048             //}
4049             //if (!cfg.name.length) {
4050             //    delete cfg.name;
4051            // }
4052             if (this.cls) {
4053                 cfg.cls += ' ' + this.cls;
4054             }
4055             if (this.style) {
4056                 cfg.style = this.style;
4057             }
4058             this.el = Roo.get(document.body).createChild(cfg, position);
4059         }
4060         //var type = this.el.dom.type;
4061
4062
4063         if(this.tabIndex !== undefined){
4064             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4065         }
4066
4067         this.dialogEl = this.el.select('.modal-dialog',true).first();
4068         this.bodyEl = this.el.select('.modal-body',true).first();
4069         this.closeEl = this.el.select('.modal-header .close', true).first();
4070         this.headerEl = this.el.select('.modal-header',true).first();
4071         this.titleEl = this.el.select('.modal-title',true).first();
4072         this.footerEl = this.el.select('.modal-footer',true).first();
4073
4074         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4075         
4076         //this.el.addClass("x-dlg-modal");
4077
4078         if (this.buttons.length) {
4079             Roo.each(this.buttons, function(bb) {
4080                 var b = Roo.apply({}, bb);
4081                 b.xns = b.xns || Roo.bootstrap;
4082                 b.xtype = b.xtype || 'Button';
4083                 if (typeof(b.listeners) == 'undefined') {
4084                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4085                 }
4086
4087                 var btn = Roo.factory(b);
4088
4089                 btn.render(this.getButtonContainer());
4090
4091             },this);
4092         }
4093         // render the children.
4094         var nitems = [];
4095
4096         if(typeof(this.items) != 'undefined'){
4097             var items = this.items;
4098             delete this.items;
4099
4100             for(var i =0;i < items.length;i++) {
4101                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4102             }
4103         }
4104
4105         this.items = nitems;
4106
4107         // where are these used - they used to be body/close/footer
4108
4109
4110         this.initEvents();
4111         //this.el.addClass([this.fieldClass, this.cls]);
4112
4113     },
4114
4115     getAutoCreate : function()
4116     {
4117         // we will default to modal-body-overflow - might need to remove or make optional later.
4118         var bdy = {
4119                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4120                 html : this.html || ''
4121         };
4122
4123         var title = {
4124             tag: 'h5',
4125             cls : 'modal-title',
4126             html : this.title
4127         };
4128
4129         if(this.specificTitle){ // WTF is this?
4130             title = this.title;
4131         }
4132
4133         var header = [];
4134         if (this.allow_close && Roo.bootstrap.version == 3) {
4135             header.push({
4136                 tag: 'button',
4137                 cls : 'close',
4138                 html : '&times'
4139             });
4140         }
4141
4142         header.push(title);
4143
4144         if (this.editableTitle) {
4145             header.push({
4146                 cls: 'form-control roo-editable-title d-none',
4147                 tag: 'input',
4148                 type: 'text'
4149             });
4150         }
4151         
4152         if (this.allow_close && Roo.bootstrap.version == 4) {
4153             header.push({
4154                 tag: 'button',
4155                 cls : 'close',
4156                 html : '&times'
4157             });
4158         }
4159         
4160         var size = '';
4161
4162         if(this.size.length){
4163             size = 'modal-' + this.size;
4164         }
4165         
4166         var footer = Roo.bootstrap.version == 3 ?
4167             {
4168                 cls : 'modal-footer',
4169                 cn : [
4170                     {
4171                         tag: 'div',
4172                         cls: 'btn-' + this.buttonPosition
4173                     }
4174                 ]
4175
4176             } :
4177             {  // BS4 uses mr-auto on left buttons....
4178                 cls : 'modal-footer'
4179             };
4180
4181             
4182
4183         
4184         
4185         var modal = {
4186             cls: "modal",
4187              cn : [
4188                 {
4189                     cls: "modal-dialog " + size,
4190                     cn : [
4191                         {
4192                             cls : "modal-content",
4193                             cn : [
4194                                 {
4195                                     cls : 'modal-header',
4196                                     cn : header
4197                                 },
4198                                 bdy,
4199                                 footer
4200                             ]
4201
4202                         }
4203                     ]
4204
4205                 }
4206             ]
4207         };
4208
4209         if(this.animate){
4210             modal.cls += ' fade';
4211         }
4212
4213         return modal;
4214
4215     },
4216     getChildContainer : function() {
4217
4218          return this.bodyEl;
4219
4220     },
4221     getButtonContainer : function() {
4222         
4223          return Roo.bootstrap.version == 4 ?
4224             this.el.select('.modal-footer',true).first()
4225             : this.el.select('.modal-footer div',true).first();
4226
4227     },
4228     initEvents : function()
4229     {
4230         if (this.allow_close) {
4231             this.closeEl.on('click', this.hide, this);
4232         }
4233         Roo.EventManager.onWindowResize(this.resize, this, true);
4234         if (this.editableTitle) {
4235             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4236             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4237             this.headerEditEl.on('keyup', function(e) {
4238                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4239                         this.toggleHeaderInput(false)
4240                     }
4241                 }, this);
4242             this.headerEditEl.on('blur', function(e) {
4243                 this.toggleHeaderInput(false)
4244             },this);
4245         }
4246
4247     },
4248   
4249
4250     resize : function()
4251     {
4252         this.maskEl.setSize(
4253             Roo.lib.Dom.getViewWidth(true),
4254             Roo.lib.Dom.getViewHeight(true)
4255         );
4256         
4257         if (this.fitwindow) {
4258             
4259            this.dialogEl.setStyle( { 'max-width' : '100%' });
4260             this.setSize(
4261                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4262                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4263             );
4264             return;
4265         }
4266         
4267         if(this.max_width !== 0) {
4268             
4269             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4270             
4271             if(this.height) {
4272                 this.setSize(w, this.height);
4273                 return;
4274             }
4275             
4276             if(this.max_height) {
4277                 this.setSize(w,Math.min(
4278                     this.max_height,
4279                     Roo.lib.Dom.getViewportHeight(true) - 60
4280                 ));
4281                 
4282                 return;
4283             }
4284             
4285             if(!this.fit_content) {
4286                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4287                 return;
4288             }
4289             
4290             this.setSize(w, Math.min(
4291                 60 +
4292                 this.headerEl.getHeight() + 
4293                 this.footerEl.getHeight() + 
4294                 this.getChildHeight(this.bodyEl.dom.childNodes),
4295                 Roo.lib.Dom.getViewportHeight(true) - 60)
4296             );
4297         }
4298         
4299     },
4300
4301     setSize : function(w,h)
4302     {
4303         if (!w && !h) {
4304             return;
4305         }
4306         
4307         this.resizeTo(w,h);
4308     },
4309
4310     show : function() {
4311
4312         if (!this.rendered) {
4313             this.render();
4314         }
4315         this.toggleHeaderInput(false);
4316         //this.el.setStyle('display', 'block');
4317         this.el.removeClass('hideing');
4318         this.el.dom.style.display='block';
4319         
4320         Roo.get(document.body).addClass('modal-open');
4321  
4322         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4323             
4324             (function(){
4325                 this.el.addClass('show');
4326                 this.el.addClass('in');
4327             }).defer(50, this);
4328         }else{
4329             this.el.addClass('show');
4330             this.el.addClass('in');
4331         }
4332
4333         // not sure how we can show data in here..
4334         //if (this.tmpl) {
4335         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4336         //}
4337
4338         Roo.get(document.body).addClass("x-body-masked");
4339         
4340         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4341         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4342         this.maskEl.dom.style.display = 'block';
4343         this.maskEl.addClass('show');
4344         
4345         
4346         this.resize();
4347         
4348         this.fireEvent('show', this);
4349
4350         // set zindex here - otherwise it appears to be ignored...
4351         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4352
4353         (function () {
4354             this.items.forEach( function(e) {
4355                 e.layout ? e.layout() : false;
4356
4357             });
4358         }).defer(100,this);
4359
4360     },
4361     hide : function()
4362     {
4363         if(this.fireEvent("beforehide", this) !== false){
4364             
4365             this.maskEl.removeClass('show');
4366             
4367             this.maskEl.dom.style.display = '';
4368             Roo.get(document.body).removeClass("x-body-masked");
4369             this.el.removeClass('in');
4370             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4371
4372             if(this.animate){ // why
4373                 this.el.addClass('hideing');
4374                 this.el.removeClass('show');
4375                 (function(){
4376                     if (!this.el.hasClass('hideing')) {
4377                         return; // it's been shown again...
4378                     }
4379                     
4380                     this.el.dom.style.display='';
4381
4382                     Roo.get(document.body).removeClass('modal-open');
4383                     this.el.removeClass('hideing');
4384                 }).defer(150,this);
4385                 
4386             }else{
4387                 this.el.removeClass('show');
4388                 this.el.dom.style.display='';
4389                 Roo.get(document.body).removeClass('modal-open');
4390
4391             }
4392             this.fireEvent('hide', this);
4393         }
4394     },
4395     isVisible : function()
4396     {
4397         
4398         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4399         
4400     },
4401
4402     addButton : function(str, cb)
4403     {
4404
4405
4406         var b = Roo.apply({}, { html : str } );
4407         b.xns = b.xns || Roo.bootstrap;
4408         b.xtype = b.xtype || 'Button';
4409         if (typeof(b.listeners) == 'undefined') {
4410             b.listeners = { click : cb.createDelegate(this)  };
4411         }
4412
4413         var btn = Roo.factory(b);
4414
4415         btn.render(this.getButtonContainer());
4416
4417         return btn;
4418
4419     },
4420
4421     setDefaultButton : function(btn)
4422     {
4423         //this.el.select('.modal-footer').()
4424     },
4425
4426     resizeTo: function(w,h)
4427     {
4428         this.dialogEl.setWidth(w);
4429         
4430         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4431
4432         this.bodyEl.setHeight(h - diff);
4433         
4434         this.fireEvent('resize', this);
4435     },
4436     
4437     setContentSize  : function(w, h)
4438     {
4439
4440     },
4441     onButtonClick: function(btn,e)
4442     {
4443         //Roo.log([a,b,c]);
4444         this.fireEvent('btnclick', btn.name, e);
4445     },
4446      /**
4447      * Set the title of the Dialog
4448      * @param {String} str new Title
4449      */
4450     setTitle: function(str) {
4451         this.titleEl.dom.innerHTML = str;
4452         this.title = str;
4453     },
4454     /**
4455      * Set the body of the Dialog
4456      * @param {String} str new Title
4457      */
4458     setBody: function(str) {
4459         this.bodyEl.dom.innerHTML = str;
4460     },
4461     /**
4462      * Set the body of the Dialog using the template
4463      * @param {Obj} data - apply this data to the template and replace the body contents.
4464      */
4465     applyBody: function(obj)
4466     {
4467         if (!this.tmpl) {
4468             Roo.log("Error - using apply Body without a template");
4469             //code
4470         }
4471         this.tmpl.overwrite(this.bodyEl, obj);
4472     },
4473     
4474     getChildHeight : function(child_nodes)
4475     {
4476         if(
4477             !child_nodes ||
4478             child_nodes.length == 0
4479         ) {
4480             return 0;
4481         }
4482         
4483         var child_height = 0;
4484         
4485         for(var i = 0; i < child_nodes.length; i++) {
4486             
4487             /*
4488             * for modal with tabs...
4489             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4490                 
4491                 var layout_childs = child_nodes[i].childNodes;
4492                 
4493                 for(var j = 0; j < layout_childs.length; j++) {
4494                     
4495                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4496                         
4497                         var layout_body_childs = layout_childs[j].childNodes;
4498                         
4499                         for(var k = 0; k < layout_body_childs.length; k++) {
4500                             
4501                             if(layout_body_childs[k].classList.contains('navbar')) {
4502                                 child_height += layout_body_childs[k].offsetHeight;
4503                                 continue;
4504                             }
4505                             
4506                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4507                                 
4508                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4509                                 
4510                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4511                                     
4512                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4513                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4514                                         continue;
4515                                     }
4516                                     
4517                                 }
4518                                 
4519                             }
4520                             
4521                         }
4522                     }
4523                 }
4524                 continue;
4525             }
4526             */
4527             
4528             child_height += child_nodes[i].offsetHeight;
4529             // Roo.log(child_nodes[i].offsetHeight);
4530         }
4531         
4532         return child_height;
4533     },
4534     toggleHeaderInput : function(is_edit)
4535     {
4536         if (!this.editableTitle) {
4537             return; // not editable.
4538         }
4539         if (is_edit && this.is_header_editing) {
4540             return; // already editing..
4541         }
4542         if (is_edit) {
4543     
4544             this.headerEditEl.dom.value = this.title;
4545             this.headerEditEl.removeClass('d-none');
4546             this.headerEditEl.dom.focus();
4547             this.titleEl.addClass('d-none');
4548             
4549             this.is_header_editing = true;
4550             return
4551         }
4552         // flip back to not editing.
4553         this.title = this.headerEditEl.dom.value;
4554         this.headerEditEl.addClass('d-none');
4555         this.titleEl.removeClass('d-none');
4556         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4557         this.is_header_editing = false;
4558         this.fireEvent('titlechanged', this, this.title);
4559     
4560             
4561         
4562     }
4563
4564 });
4565
4566
4567 Roo.apply(Roo.bootstrap.Modal,  {
4568     /**
4569          * Button config that displays a single OK button
4570          * @type Object
4571          */
4572         OK :  [{
4573             name : 'ok',
4574             weight : 'primary',
4575             html : 'OK'
4576         }],
4577         /**
4578          * Button config that displays Yes and No buttons
4579          * @type Object
4580          */
4581         YESNO : [
4582             {
4583                 name  : 'no',
4584                 html : 'No'
4585             },
4586             {
4587                 name  :'yes',
4588                 weight : 'primary',
4589                 html : 'Yes'
4590             }
4591         ],
4592
4593         /**
4594          * Button config that displays OK and Cancel buttons
4595          * @type Object
4596          */
4597         OKCANCEL : [
4598             {
4599                name : 'cancel',
4600                 html : 'Cancel'
4601             },
4602             {
4603                 name : 'ok',
4604                 weight : 'primary',
4605                 html : 'OK'
4606             }
4607         ],
4608         /**
4609          * Button config that displays Yes, No and Cancel buttons
4610          * @type Object
4611          */
4612         YESNOCANCEL : [
4613             {
4614                 name : 'yes',
4615                 weight : 'primary',
4616                 html : 'Yes'
4617             },
4618             {
4619                 name : 'no',
4620                 html : 'No'
4621             },
4622             {
4623                 name : 'cancel',
4624                 html : 'Cancel'
4625             }
4626         ],
4627         
4628         zIndex : 10001
4629 });
4630
4631 /*
4632  * - LGPL
4633  *
4634  * messagebox - can be used as a replace
4635  * 
4636  */
4637 /**
4638  * @class Roo.MessageBox
4639  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4640  * Example usage:
4641  *<pre><code>
4642 // Basic alert:
4643 Roo.Msg.alert('Status', 'Changes saved successfully.');
4644
4645 // Prompt for user data:
4646 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4647     if (btn == 'ok'){
4648         // process text value...
4649     }
4650 });
4651
4652 // Show a dialog using config options:
4653 Roo.Msg.show({
4654    title:'Save Changes?',
4655    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4656    buttons: Roo.Msg.YESNOCANCEL,
4657    fn: processResult,
4658    animEl: 'elId'
4659 });
4660 </code></pre>
4661  * @singleton
4662  */
4663 Roo.bootstrap.MessageBox = function(){
4664     var dlg, opt, mask, waitTimer;
4665     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4666     var buttons, activeTextEl, bwidth;
4667
4668     
4669     // private
4670     var handleButton = function(button){
4671         dlg.hide();
4672         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4673     };
4674
4675     // private
4676     var handleHide = function(){
4677         if(opt && opt.cls){
4678             dlg.el.removeClass(opt.cls);
4679         }
4680         //if(waitTimer){
4681         //    Roo.TaskMgr.stop(waitTimer);
4682         //    waitTimer = null;
4683         //}
4684     };
4685
4686     // private
4687     var updateButtons = function(b){
4688         var width = 0;
4689         if(!b){
4690             buttons["ok"].hide();
4691             buttons["cancel"].hide();
4692             buttons["yes"].hide();
4693             buttons["no"].hide();
4694             dlg.footerEl.hide();
4695             
4696             return width;
4697         }
4698         dlg.footerEl.show();
4699         for(var k in buttons){
4700             if(typeof buttons[k] != "function"){
4701                 if(b[k]){
4702                     buttons[k].show();
4703                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4704                     width += buttons[k].el.getWidth()+15;
4705                 }else{
4706                     buttons[k].hide();
4707                 }
4708             }
4709         }
4710         return width;
4711     };
4712
4713     // private
4714     var handleEsc = function(d, k, e){
4715         if(opt && opt.closable !== false){
4716             dlg.hide();
4717         }
4718         if(e){
4719             e.stopEvent();
4720         }
4721     };
4722
4723     return {
4724         /**
4725          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4726          * @return {Roo.BasicDialog} The BasicDialog element
4727          */
4728         getDialog : function(){
4729            if(!dlg){
4730                 dlg = new Roo.bootstrap.Modal( {
4731                     //draggable: true,
4732                     //resizable:false,
4733                     //constraintoviewport:false,
4734                     //fixedcenter:true,
4735                     //collapsible : false,
4736                     //shim:true,
4737                     //modal: true,
4738                 //    width: 'auto',
4739                   //  height:100,
4740                     //buttonAlign:"center",
4741                     closeClick : function(){
4742                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4743                             handleButton("no");
4744                         }else{
4745                             handleButton("cancel");
4746                         }
4747                     }
4748                 });
4749                 dlg.render();
4750                 dlg.on("hide", handleHide);
4751                 mask = dlg.mask;
4752                 //dlg.addKeyListener(27, handleEsc);
4753                 buttons = {};
4754                 this.buttons = buttons;
4755                 var bt = this.buttonText;
4756                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4757                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4758                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4759                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4760                 //Roo.log(buttons);
4761                 bodyEl = dlg.bodyEl.createChild({
4762
4763                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4764                         '<textarea class="roo-mb-textarea"></textarea>' +
4765                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4766                 });
4767                 msgEl = bodyEl.dom.firstChild;
4768                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4769                 textboxEl.enableDisplayMode();
4770                 textboxEl.addKeyListener([10,13], function(){
4771                     if(dlg.isVisible() && opt && opt.buttons){
4772                         if(opt.buttons.ok){
4773                             handleButton("ok");
4774                         }else if(opt.buttons.yes){
4775                             handleButton("yes");
4776                         }
4777                     }
4778                 });
4779                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4780                 textareaEl.enableDisplayMode();
4781                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4782                 progressEl.enableDisplayMode();
4783                 
4784                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4785                 var pf = progressEl.dom.firstChild;
4786                 if (pf) {
4787                     pp = Roo.get(pf.firstChild);
4788                     pp.setHeight(pf.offsetHeight);
4789                 }
4790                 
4791             }
4792             return dlg;
4793         },
4794
4795         /**
4796          * Updates the message box body text
4797          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4798          * the XHTML-compliant non-breaking space character '&amp;#160;')
4799          * @return {Roo.MessageBox} This message box
4800          */
4801         updateText : function(text)
4802         {
4803             if(!dlg.isVisible() && !opt.width){
4804                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4805                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4806             }
4807             msgEl.innerHTML = text || '&#160;';
4808       
4809             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4810             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4811             var w = Math.max(
4812                     Math.min(opt.width || cw , this.maxWidth), 
4813                     Math.max(opt.minWidth || this.minWidth, bwidth)
4814             );
4815             if(opt.prompt){
4816                 activeTextEl.setWidth(w);
4817             }
4818             if(dlg.isVisible()){
4819                 dlg.fixedcenter = false;
4820             }
4821             // to big, make it scroll. = But as usual stupid IE does not support
4822             // !important..
4823             
4824             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4825                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4826                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4827             } else {
4828                 bodyEl.dom.style.height = '';
4829                 bodyEl.dom.style.overflowY = '';
4830             }
4831             if (cw > w) {
4832                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4833             } else {
4834                 bodyEl.dom.style.overflowX = '';
4835             }
4836             
4837             dlg.setContentSize(w, bodyEl.getHeight());
4838             if(dlg.isVisible()){
4839                 dlg.fixedcenter = true;
4840             }
4841             return this;
4842         },
4843
4844         /**
4845          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4846          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4847          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4848          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4849          * @return {Roo.MessageBox} This message box
4850          */
4851         updateProgress : function(value, text){
4852             if(text){
4853                 this.updateText(text);
4854             }
4855             
4856             if (pp) { // weird bug on my firefox - for some reason this is not defined
4857                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4858                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4859             }
4860             return this;
4861         },        
4862
4863         /**
4864          * Returns true if the message box is currently displayed
4865          * @return {Boolean} True if the message box is visible, else false
4866          */
4867         isVisible : function(){
4868             return dlg && dlg.isVisible();  
4869         },
4870
4871         /**
4872          * Hides the message box if it is displayed
4873          */
4874         hide : function(){
4875             if(this.isVisible()){
4876                 dlg.hide();
4877             }  
4878         },
4879
4880         /**
4881          * Displays a new message box, or reinitializes an existing message box, based on the config options
4882          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4883          * The following config object properties are supported:
4884          * <pre>
4885 Property    Type             Description
4886 ----------  ---------------  ------------------------------------------------------------------------------------
4887 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4888                                    closes (defaults to undefined)
4889 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4890                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4891 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4892                                    progress and wait dialogs will ignore this property and always hide the
4893                                    close button as they can only be closed programmatically.
4894 cls               String           A custom CSS class to apply to the message box element
4895 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4896                                    displayed (defaults to 75)
4897 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4898                                    function will be btn (the name of the button that was clicked, if applicable,
4899                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4900                                    Progress and wait dialogs will ignore this option since they do not respond to
4901                                    user actions and can only be closed programmatically, so any required function
4902                                    should be called by the same code after it closes the dialog.
4903 icon              String           A CSS class that provides a background image to be used as an icon for
4904                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4905 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4906 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4907 modal             Boolean          False to allow user interaction with the page while the message box is
4908                                    displayed (defaults to true)
4909 msg               String           A string that will replace the existing message box body text (defaults
4910                                    to the XHTML-compliant non-breaking space character '&#160;')
4911 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4912 progress          Boolean          True to display a progress bar (defaults to false)
4913 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4914 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4915 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4916 title             String           The title text
4917 value             String           The string value to set into the active textbox element if displayed
4918 wait              Boolean          True to display a progress bar (defaults to false)
4919 width             Number           The width of the dialog in pixels
4920 </pre>
4921          *
4922          * Example usage:
4923          * <pre><code>
4924 Roo.Msg.show({
4925    title: 'Address',
4926    msg: 'Please enter your address:',
4927    width: 300,
4928    buttons: Roo.MessageBox.OKCANCEL,
4929    multiline: true,
4930    fn: saveAddress,
4931    animEl: 'addAddressBtn'
4932 });
4933 </code></pre>
4934          * @param {Object} config Configuration options
4935          * @return {Roo.MessageBox} This message box
4936          */
4937         show : function(options)
4938         {
4939             
4940             // this causes nightmares if you show one dialog after another
4941             // especially on callbacks..
4942              
4943             if(this.isVisible()){
4944                 
4945                 this.hide();
4946                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4947                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4948                 Roo.log("New Dialog Message:" +  options.msg )
4949                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4950                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4951                 
4952             }
4953             var d = this.getDialog();
4954             opt = options;
4955             d.setTitle(opt.title || "&#160;");
4956             d.closeEl.setDisplayed(opt.closable !== false);
4957             activeTextEl = textboxEl;
4958             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4959             if(opt.prompt){
4960                 if(opt.multiline){
4961                     textboxEl.hide();
4962                     textareaEl.show();
4963                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4964                         opt.multiline : this.defaultTextHeight);
4965                     activeTextEl = textareaEl;
4966                 }else{
4967                     textboxEl.show();
4968                     textareaEl.hide();
4969                 }
4970             }else{
4971                 textboxEl.hide();
4972                 textareaEl.hide();
4973             }
4974             progressEl.setDisplayed(opt.progress === true);
4975             if (opt.progress) {
4976                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4977             }
4978             this.updateProgress(0);
4979             activeTextEl.dom.value = opt.value || "";
4980             if(opt.prompt){
4981                 dlg.setDefaultButton(activeTextEl);
4982             }else{
4983                 var bs = opt.buttons;
4984                 var db = null;
4985                 if(bs && bs.ok){
4986                     db = buttons["ok"];
4987                 }else if(bs && bs.yes){
4988                     db = buttons["yes"];
4989                 }
4990                 dlg.setDefaultButton(db);
4991             }
4992             bwidth = updateButtons(opt.buttons);
4993             this.updateText(opt.msg);
4994             if(opt.cls){
4995                 d.el.addClass(opt.cls);
4996             }
4997             d.proxyDrag = opt.proxyDrag === true;
4998             d.modal = opt.modal !== false;
4999             d.mask = opt.modal !== false ? mask : false;
5000             if(!d.isVisible()){
5001                 // force it to the end of the z-index stack so it gets a cursor in FF
5002                 document.body.appendChild(dlg.el.dom);
5003                 d.animateTarget = null;
5004                 d.show(options.animEl);
5005             }
5006             return this;
5007         },
5008
5009         /**
5010          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5011          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5012          * and closing the message box when the process is complete.
5013          * @param {String} title The title bar text
5014          * @param {String} msg The message box body text
5015          * @return {Roo.MessageBox} This message box
5016          */
5017         progress : function(title, msg){
5018             this.show({
5019                 title : title,
5020                 msg : msg,
5021                 buttons: false,
5022                 progress:true,
5023                 closable:false,
5024                 minWidth: this.minProgressWidth,
5025                 modal : true
5026             });
5027             return this;
5028         },
5029
5030         /**
5031          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5032          * If a callback function is passed it will be called after the user clicks the button, and the
5033          * id of the button that was clicked will be passed as the only parameter to the callback
5034          * (could also be the top-right close button).
5035          * @param {String} title The title bar text
5036          * @param {String} msg The message box body text
5037          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5038          * @param {Object} scope (optional) The scope of the callback function
5039          * @return {Roo.MessageBox} This message box
5040          */
5041         alert : function(title, msg, fn, scope)
5042         {
5043             this.show({
5044                 title : title,
5045                 msg : msg,
5046                 buttons: this.OK,
5047                 fn: fn,
5048                 closable : false,
5049                 scope : scope,
5050                 modal : true
5051             });
5052             return this;
5053         },
5054
5055         /**
5056          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5057          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5058          * You are responsible for closing the message box when the process is complete.
5059          * @param {String} msg The message box body text
5060          * @param {String} title (optional) The title bar text
5061          * @return {Roo.MessageBox} This message box
5062          */
5063         wait : function(msg, title){
5064             this.show({
5065                 title : title,
5066                 msg : msg,
5067                 buttons: false,
5068                 closable:false,
5069                 progress:true,
5070                 modal:true,
5071                 width:300,
5072                 wait:true
5073             });
5074             waitTimer = Roo.TaskMgr.start({
5075                 run: function(i){
5076                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5077                 },
5078                 interval: 1000
5079             });
5080             return this;
5081         },
5082
5083         /**
5084          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5085          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5086          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5087          * @param {String} title The title bar text
5088          * @param {String} msg The message box body text
5089          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5090          * @param {Object} scope (optional) The scope of the callback function
5091          * @return {Roo.MessageBox} This message box
5092          */
5093         confirm : function(title, msg, fn, scope){
5094             this.show({
5095                 title : title,
5096                 msg : msg,
5097                 buttons: this.YESNO,
5098                 fn: fn,
5099                 scope : scope,
5100                 modal : true
5101             });
5102             return this;
5103         },
5104
5105         /**
5106          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5107          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5108          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5109          * (could also be the top-right close button) and the text that was entered will be passed as the two
5110          * parameters to the callback.
5111          * @param {String} title The title bar text
5112          * @param {String} msg The message box body text
5113          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5114          * @param {Object} scope (optional) The scope of the callback function
5115          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5116          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5117          * @return {Roo.MessageBox} This message box
5118          */
5119         prompt : function(title, msg, fn, scope, multiline){
5120             this.show({
5121                 title : title,
5122                 msg : msg,
5123                 buttons: this.OKCANCEL,
5124                 fn: fn,
5125                 minWidth:250,
5126                 scope : scope,
5127                 prompt:true,
5128                 multiline: multiline,
5129                 modal : true
5130             });
5131             return this;
5132         },
5133
5134         /**
5135          * Button config that displays a single OK button
5136          * @type Object
5137          */
5138         OK : {ok:true},
5139         /**
5140          * Button config that displays Yes and No buttons
5141          * @type Object
5142          */
5143         YESNO : {yes:true, no:true},
5144         /**
5145          * Button config that displays OK and Cancel buttons
5146          * @type Object
5147          */
5148         OKCANCEL : {ok:true, cancel:true},
5149         /**
5150          * Button config that displays Yes, No and Cancel buttons
5151          * @type Object
5152          */
5153         YESNOCANCEL : {yes:true, no:true, cancel:true},
5154
5155         /**
5156          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5157          * @type Number
5158          */
5159         defaultTextHeight : 75,
5160         /**
5161          * The maximum width in pixels of the message box (defaults to 600)
5162          * @type Number
5163          */
5164         maxWidth : 600,
5165         /**
5166          * The minimum width in pixels of the message box (defaults to 100)
5167          * @type Number
5168          */
5169         minWidth : 100,
5170         /**
5171          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5172          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5173          * @type Number
5174          */
5175         minProgressWidth : 250,
5176         /**
5177          * An object containing the default button text strings that can be overriden for localized language support.
5178          * Supported properties are: ok, cancel, yes and no.
5179          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5180          * @type Object
5181          */
5182         buttonText : {
5183             ok : "OK",
5184             cancel : "Cancel",
5185             yes : "Yes",
5186             no : "No"
5187         }
5188     };
5189 }();
5190
5191 /**
5192  * Shorthand for {@link Roo.MessageBox}
5193  */
5194 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5195 Roo.Msg = Roo.Msg || Roo.MessageBox;
5196 /*
5197  * - LGPL
5198  *
5199  * navbar
5200  * 
5201  */
5202
5203 /**
5204  * @class Roo.bootstrap.Navbar
5205  * @extends Roo.bootstrap.Component
5206  * Bootstrap Navbar class
5207
5208  * @constructor
5209  * Create a new Navbar
5210  * @param {Object} config The config object
5211  */
5212
5213
5214 Roo.bootstrap.Navbar = function(config){
5215     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5216     this.addEvents({
5217         // raw events
5218         /**
5219          * @event beforetoggle
5220          * Fire before toggle the menu
5221          * @param {Roo.EventObject} e
5222          */
5223         "beforetoggle" : true
5224     });
5225 };
5226
5227 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5228     
5229     
5230    
5231     // private
5232     navItems : false,
5233     loadMask : false,
5234     
5235     
5236     getAutoCreate : function(){
5237         
5238         
5239         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5240         
5241     },
5242     
5243     initEvents :function ()
5244     {
5245         //Roo.log(this.el.select('.navbar-toggle',true));
5246         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5247         
5248         var mark = {
5249             tag: "div",
5250             cls:"x-dlg-mask"
5251         };
5252         
5253         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5254         
5255         var size = this.el.getSize();
5256         this.maskEl.setSize(size.width, size.height);
5257         this.maskEl.enableDisplayMode("block");
5258         this.maskEl.hide();
5259         
5260         if(this.loadMask){
5261             this.maskEl.show();
5262         }
5263     },
5264     
5265     
5266     getChildContainer : function()
5267     {
5268         if (this.el && this.el.select('.collapse').getCount()) {
5269             return this.el.select('.collapse',true).first();
5270         }
5271         
5272         return this.el;
5273     },
5274     
5275     mask : function()
5276     {
5277         this.maskEl.show();
5278     },
5279     
5280     unmask : function()
5281     {
5282         this.maskEl.hide();
5283     },
5284     onToggle : function()
5285     {
5286         
5287         if(this.fireEvent('beforetoggle', this) === false){
5288             return;
5289         }
5290         var ce = this.el.select('.navbar-collapse',true).first();
5291       
5292         if (!ce.hasClass('show')) {
5293            this.expand();
5294         } else {
5295             this.collapse();
5296         }
5297         
5298         
5299     
5300     },
5301     /**
5302      * Expand the navbar pulldown 
5303      */
5304     expand : function ()
5305     {
5306        
5307         var ce = this.el.select('.navbar-collapse',true).first();
5308         if (ce.hasClass('collapsing')) {
5309             return;
5310         }
5311         ce.dom.style.height = '';
5312                // show it...
5313         ce.addClass('in'); // old...
5314         ce.removeClass('collapse');
5315         ce.addClass('show');
5316         var h = ce.getHeight();
5317         Roo.log(h);
5318         ce.removeClass('show');
5319         // at this point we should be able to see it..
5320         ce.addClass('collapsing');
5321         
5322         ce.setHeight(0); // resize it ...
5323         ce.on('transitionend', function() {
5324             //Roo.log('done transition');
5325             ce.removeClass('collapsing');
5326             ce.addClass('show');
5327             ce.removeClass('collapse');
5328
5329             ce.dom.style.height = '';
5330         }, this, { single: true} );
5331         ce.setHeight(h);
5332         ce.dom.scrollTop = 0;
5333     },
5334     /**
5335      * Collapse the navbar pulldown 
5336      */
5337     collapse : function()
5338     {
5339          var ce = this.el.select('.navbar-collapse',true).first();
5340        
5341         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5342             // it's collapsed or collapsing..
5343             return;
5344         }
5345         ce.removeClass('in'); // old...
5346         ce.setHeight(ce.getHeight());
5347         ce.removeClass('show');
5348         ce.addClass('collapsing');
5349         
5350         ce.on('transitionend', function() {
5351             ce.dom.style.height = '';
5352             ce.removeClass('collapsing');
5353             ce.addClass('collapse');
5354         }, this, { single: true} );
5355         ce.setHeight(0);
5356     }
5357     
5358     
5359     
5360 });
5361
5362
5363
5364  
5365
5366  /*
5367  * - LGPL
5368  *
5369  * navbar
5370  * 
5371  */
5372
5373 /**
5374  * @class Roo.bootstrap.NavSimplebar
5375  * @extends Roo.bootstrap.Navbar
5376  * Bootstrap Sidebar class
5377  *
5378  * @cfg {Boolean} inverse is inverted color
5379  * 
5380  * @cfg {String} type (nav | pills | tabs)
5381  * @cfg {Boolean} arrangement stacked | justified
5382  * @cfg {String} align (left | right) alignment
5383  * 
5384  * @cfg {Boolean} main (true|false) main nav bar? default false
5385  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5386  * 
5387  * @cfg {String} tag (header|footer|nav|div) default is nav 
5388
5389  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5390  * 
5391  * 
5392  * @constructor
5393  * Create a new Sidebar
5394  * @param {Object} config The config object
5395  */
5396
5397
5398 Roo.bootstrap.NavSimplebar = function(config){
5399     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5400 };
5401
5402 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5403     
5404     inverse: false,
5405     
5406     type: false,
5407     arrangement: '',
5408     align : false,
5409     
5410     weight : 'light',
5411     
5412     main : false,
5413     
5414     
5415     tag : false,
5416     
5417     
5418     getAutoCreate : function(){
5419         
5420         
5421         var cfg = {
5422             tag : this.tag || 'div',
5423             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5424         };
5425         if (['light','white'].indexOf(this.weight) > -1) {
5426             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5427         }
5428         cfg.cls += ' bg-' + this.weight;
5429         
5430         if (this.inverse) {
5431             cfg.cls += ' navbar-inverse';
5432             
5433         }
5434         
5435         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5436         
5437         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5438             return cfg;
5439         }
5440         
5441         
5442     
5443         
5444         cfg.cn = [
5445             {
5446                 cls: 'nav nav-' + this.xtype,
5447                 tag : 'ul'
5448             }
5449         ];
5450         
5451          
5452         this.type = this.type || 'nav';
5453         if (['tabs','pills'].indexOf(this.type) != -1) {
5454             cfg.cn[0].cls += ' nav-' + this.type
5455         
5456         
5457         } else {
5458             if (this.type!=='nav') {
5459                 Roo.log('nav type must be nav/tabs/pills')
5460             }
5461             cfg.cn[0].cls += ' navbar-nav'
5462         }
5463         
5464         
5465         
5466         
5467         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5468             cfg.cn[0].cls += ' nav-' + this.arrangement;
5469         }
5470         
5471         
5472         if (this.align === 'right') {
5473             cfg.cn[0].cls += ' navbar-right';
5474         }
5475         
5476         
5477         
5478         
5479         return cfg;
5480     
5481         
5482     }
5483     
5484     
5485     
5486 });
5487
5488
5489
5490  
5491
5492  
5493        /*
5494  * - LGPL
5495  *
5496  * navbar
5497  * navbar-fixed-top
5498  * navbar-expand-md  fixed-top 
5499  */
5500
5501 /**
5502  * @class Roo.bootstrap.NavHeaderbar
5503  * @extends Roo.bootstrap.NavSimplebar
5504  * Bootstrap Sidebar class
5505  *
5506  * @cfg {String} brand what is brand
5507  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5508  * @cfg {String} brand_href href of the brand
5509  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5510  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5511  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5512  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5513  * 
5514  * @constructor
5515  * Create a new Sidebar
5516  * @param {Object} config The config object
5517  */
5518
5519
5520 Roo.bootstrap.NavHeaderbar = function(config){
5521     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5522       
5523 };
5524
5525 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5526     
5527     position: '',
5528     brand: '',
5529     brand_href: false,
5530     srButton : true,
5531     autohide : false,
5532     desktopCenter : false,
5533    
5534     
5535     getAutoCreate : function(){
5536         
5537         var   cfg = {
5538             tag: this.nav || 'nav',
5539             cls: 'navbar navbar-expand-md',
5540             role: 'navigation',
5541             cn: []
5542         };
5543         
5544         var cn = cfg.cn;
5545         if (this.desktopCenter) {
5546             cn.push({cls : 'container', cn : []});
5547             cn = cn[0].cn;
5548         }
5549         
5550         if(this.srButton){
5551             var btn = {
5552                 tag: 'button',
5553                 type: 'button',
5554                 cls: 'navbar-toggle navbar-toggler',
5555                 'data-toggle': 'collapse',
5556                 cn: [
5557                     {
5558                         tag: 'span',
5559                         cls: 'sr-only',
5560                         html: 'Toggle navigation'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar navbar-toggler-icon'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     },
5570                     {
5571                         tag: 'span',
5572                         cls: 'icon-bar'
5573                     }
5574                 ]
5575             };
5576             
5577             cn.push( Roo.bootstrap.version == 4 ? btn : {
5578                 tag: 'div',
5579                 cls: 'navbar-header',
5580                 cn: [
5581                     btn
5582                 ]
5583             });
5584         }
5585         
5586         cn.push({
5587             tag: 'div',
5588             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5589             cn : []
5590         });
5591         
5592         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5593         
5594         if (['light','white'].indexOf(this.weight) > -1) {
5595             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5596         }
5597         cfg.cls += ' bg-' + this.weight;
5598         
5599         
5600         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5601             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5602             
5603             // tag can override this..
5604             
5605             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5606         }
5607         
5608         if (this.brand !== '') {
5609             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5610             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5611                 tag: 'a',
5612                 href: this.brand_href ? this.brand_href : '#',
5613                 cls: 'navbar-brand',
5614                 cn: [
5615                 this.brand
5616                 ]
5617             });
5618         }
5619         
5620         if(this.main){
5621             cfg.cls += ' main-nav';
5622         }
5623         
5624         
5625         return cfg;
5626
5627         
5628     },
5629     getHeaderChildContainer : function()
5630     {
5631         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5632             return this.el.select('.navbar-header',true).first();
5633         }
5634         
5635         return this.getChildContainer();
5636     },
5637     
5638     getChildContainer : function()
5639     {
5640          
5641         return this.el.select('.roo-navbar-collapse',true).first();
5642          
5643         
5644     },
5645     
5646     initEvents : function()
5647     {
5648         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5649         
5650         if (this.autohide) {
5651             
5652             var prevScroll = 0;
5653             var ft = this.el;
5654             
5655             Roo.get(document).on('scroll',function(e) {
5656                 var ns = Roo.get(document).getScroll().top;
5657                 var os = prevScroll;
5658                 prevScroll = ns;
5659                 
5660                 if(ns > os){
5661                     ft.removeClass('slideDown');
5662                     ft.addClass('slideUp');
5663                     return;
5664                 }
5665                 ft.removeClass('slideUp');
5666                 ft.addClass('slideDown');
5667                  
5668               
5669           },this);
5670         }
5671     }    
5672     
5673 });
5674
5675
5676
5677  
5678
5679  /*
5680  * - LGPL
5681  *
5682  * navbar
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.NavSidebar
5688  * @extends Roo.bootstrap.Navbar
5689  * Bootstrap Sidebar class
5690  * 
5691  * @constructor
5692  * Create a new Sidebar
5693  * @param {Object} config The config object
5694  */
5695
5696
5697 Roo.bootstrap.NavSidebar = function(config){
5698     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5699 };
5700
5701 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5702     
5703     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5704     
5705     getAutoCreate : function(){
5706         
5707         
5708         return  {
5709             tag: 'div',
5710             cls: 'sidebar sidebar-nav'
5711         };
5712     
5713         
5714     }
5715     
5716     
5717     
5718 });
5719
5720
5721
5722  
5723
5724  /*
5725  * - LGPL
5726  *
5727  * nav group
5728  * 
5729  */
5730
5731 /**
5732  * @class Roo.bootstrap.NavGroup
5733  * @extends Roo.bootstrap.Component
5734  * Bootstrap NavGroup class
5735  * @cfg {String} align (left|right)
5736  * @cfg {Boolean} inverse
5737  * @cfg {String} type (nav|pills|tab) default nav
5738  * @cfg {String} navId - reference Id for navbar.
5739  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5740  * 
5741  * @constructor
5742  * Create a new nav group
5743  * @param {Object} config The config object
5744  */
5745
5746 Roo.bootstrap.NavGroup = function(config){
5747     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5748     this.navItems = [];
5749    
5750     Roo.bootstrap.NavGroup.register(this);
5751      this.addEvents({
5752         /**
5753              * @event changed
5754              * Fires when the active item changes
5755              * @param {Roo.bootstrap.NavGroup} this
5756              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5757              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5758          */
5759         'changed': true
5760      });
5761     
5762 };
5763
5764 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5765     
5766     align: '',
5767     inverse: false,
5768     form: false,
5769     type: 'nav',
5770     navId : '',
5771     // private
5772     pilltype : true,
5773     
5774     navItems : false, 
5775     
5776     getAutoCreate : function()
5777     {
5778         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5779         
5780         cfg = {
5781             tag : 'ul',
5782             cls: 'nav' 
5783         };
5784         if (Roo.bootstrap.version == 4) {
5785             if (['tabs','pills'].indexOf(this.type) != -1) {
5786                 cfg.cls += ' nav-' + this.type; 
5787             } else {
5788                 // trying to remove so header bar can right align top?
5789                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5790                     // do not use on header bar... 
5791                     cfg.cls += ' navbar-nav';
5792                 }
5793             }
5794             
5795         } else {
5796             if (['tabs','pills'].indexOf(this.type) != -1) {
5797                 cfg.cls += ' nav-' + this.type
5798             } else {
5799                 if (this.type !== 'nav') {
5800                     Roo.log('nav type must be nav/tabs/pills')
5801                 }
5802                 cfg.cls += ' navbar-nav'
5803             }
5804         }
5805         
5806         if (this.parent() && this.parent().sidebar) {
5807             cfg = {
5808                 tag: 'ul',
5809                 cls: 'dashboard-menu sidebar-menu'
5810             };
5811             
5812             return cfg;
5813         }
5814         
5815         if (this.form === true) {
5816             cfg = {
5817                 tag: 'form',
5818                 cls: 'navbar-form form-inline'
5819             };
5820             //nav navbar-right ml-md-auto
5821             if (this.align === 'right') {
5822                 cfg.cls += ' navbar-right ml-md-auto';
5823             } else {
5824                 cfg.cls += ' navbar-left';
5825             }
5826         }
5827         
5828         if (this.align === 'right') {
5829             cfg.cls += ' navbar-right ml-md-auto';
5830         } else {
5831             cfg.cls += ' mr-auto';
5832         }
5833         
5834         if (this.inverse) {
5835             cfg.cls += ' navbar-inverse';
5836             
5837         }
5838         
5839         
5840         return cfg;
5841     },
5842     /**
5843     * sets the active Navigation item
5844     * @param {Roo.bootstrap.NavItem} the new current navitem
5845     */
5846     setActiveItem : function(item)
5847     {
5848         var prev = false;
5849         Roo.each(this.navItems, function(v){
5850             if (v == item) {
5851                 return ;
5852             }
5853             if (v.isActive()) {
5854                 v.setActive(false, true);
5855                 prev = v;
5856                 
5857             }
5858             
5859         });
5860
5861         item.setActive(true, true);
5862         this.fireEvent('changed', this, item, prev);
5863         
5864         
5865     },
5866     /**
5867     * gets the active Navigation item
5868     * @return {Roo.bootstrap.NavItem} the current navitem
5869     */
5870     getActive : function()
5871     {
5872         
5873         var prev = false;
5874         Roo.each(this.navItems, function(v){
5875             
5876             if (v.isActive()) {
5877                 prev = v;
5878                 
5879             }
5880             
5881         });
5882         return prev;
5883     },
5884     
5885     indexOfNav : function()
5886     {
5887         
5888         var prev = false;
5889         Roo.each(this.navItems, function(v,i){
5890             
5891             if (v.isActive()) {
5892                 prev = i;
5893                 
5894             }
5895             
5896         });
5897         return prev;
5898     },
5899     /**
5900     * adds a Navigation item
5901     * @param {Roo.bootstrap.NavItem} the navitem to add
5902     */
5903     addItem : function(cfg)
5904     {
5905         if (this.form && Roo.bootstrap.version == 4) {
5906             cfg.tag = 'div';
5907         }
5908         var cn = new Roo.bootstrap.NavItem(cfg);
5909         this.register(cn);
5910         cn.parentId = this.id;
5911         cn.onRender(this.el, null);
5912         return cn;
5913     },
5914     /**
5915     * register a Navigation item
5916     * @param {Roo.bootstrap.NavItem} the navitem to add
5917     */
5918     register : function(item)
5919     {
5920         this.navItems.push( item);
5921         item.navId = this.navId;
5922     
5923     },
5924     
5925     /**
5926     * clear all the Navigation item
5927     */
5928    
5929     clearAll : function()
5930     {
5931         this.navItems = [];
5932         this.el.dom.innerHTML = '';
5933     },
5934     
5935     getNavItem: function(tabId)
5936     {
5937         var ret = false;
5938         Roo.each(this.navItems, function(e) {
5939             if (e.tabId == tabId) {
5940                ret =  e;
5941                return false;
5942             }
5943             return true;
5944             
5945         });
5946         return ret;
5947     },
5948     
5949     setActiveNext : function()
5950     {
5951         var i = this.indexOfNav(this.getActive());
5952         if (i > this.navItems.length) {
5953             return;
5954         }
5955         this.setActiveItem(this.navItems[i+1]);
5956     },
5957     setActivePrev : function()
5958     {
5959         var i = this.indexOfNav(this.getActive());
5960         if (i  < 1) {
5961             return;
5962         }
5963         this.setActiveItem(this.navItems[i-1]);
5964     },
5965     clearWasActive : function(except) {
5966         Roo.each(this.navItems, function(e) {
5967             if (e.tabId != except.tabId && e.was_active) {
5968                e.was_active = false;
5969                return false;
5970             }
5971             return true;
5972             
5973         });
5974     },
5975     getWasActive : function ()
5976     {
5977         var r = false;
5978         Roo.each(this.navItems, function(e) {
5979             if (e.was_active) {
5980                r = e;
5981                return false;
5982             }
5983             return true;
5984             
5985         });
5986         return r;
5987     }
5988     
5989     
5990 });
5991
5992  
5993 Roo.apply(Roo.bootstrap.NavGroup, {
5994     
5995     groups: {},
5996      /**
5997     * register a Navigation Group
5998     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5999     */
6000     register : function(navgrp)
6001     {
6002         this.groups[navgrp.navId] = navgrp;
6003         
6004     },
6005     /**
6006     * fetch a Navigation Group based on the navigation ID
6007     * @param {string} the navgroup to add
6008     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6009     */
6010     get: function(navId) {
6011         if (typeof(this.groups[navId]) == 'undefined') {
6012             return false;
6013             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6014         }
6015         return this.groups[navId] ;
6016     }
6017     
6018     
6019     
6020 });
6021
6022  /*
6023  * - LGPL
6024  *
6025  * row
6026  * 
6027  */
6028
6029 /**
6030  * @class Roo.bootstrap.NavItem
6031  * @extends Roo.bootstrap.Component
6032  * Bootstrap Navbar.NavItem class
6033  * @cfg {String} href  link to
6034  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6035  * @cfg {Boolean} button_outline show and outlined button
6036  * @cfg {String} html content of button
6037  * @cfg {String} badge text inside badge
6038  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6039  * @cfg {String} glyphicon DEPRICATED - use fa
6040  * @cfg {String} icon DEPRICATED - use fa
6041  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6042  * @cfg {Boolean} active Is item active
6043  * @cfg {Boolean} disabled Is item disabled
6044  * @cfg {String} linkcls  Link Class
6045  * @cfg {Boolean} preventDefault (true | false) default false
6046  * @cfg {String} tabId the tab that this item activates.
6047  * @cfg {String} tagtype (a|span) render as a href or span?
6048  * @cfg {Boolean} animateRef (true|false) link to element default false  
6049   
6050  * @constructor
6051  * Create a new Navbar Item
6052  * @param {Object} config The config object
6053  */
6054 Roo.bootstrap.NavItem = function(config){
6055     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6056     this.addEvents({
6057         // raw events
6058         /**
6059          * @event click
6060          * The raw click event for the entire grid.
6061          * @param {Roo.EventObject} e
6062          */
6063         "click" : true,
6064          /**
6065             * @event changed
6066             * Fires when the active item active state changes
6067             * @param {Roo.bootstrap.NavItem} this
6068             * @param {boolean} state the new state
6069              
6070          */
6071         'changed': true,
6072         /**
6073             * @event scrollto
6074             * Fires when scroll to element
6075             * @param {Roo.bootstrap.NavItem} this
6076             * @param {Object} options
6077             * @param {Roo.EventObject} e
6078              
6079          */
6080         'scrollto': true
6081     });
6082    
6083 };
6084
6085 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6086     
6087     href: false,
6088     html: '',
6089     badge: '',
6090     icon: false,
6091     fa : false,
6092     glyphicon: false,
6093     active: false,
6094     preventDefault : false,
6095     tabId : false,
6096     tagtype : 'a',
6097     tag: 'li',
6098     disabled : false,
6099     animateRef : false,
6100     was_active : false,
6101     button_weight : '',
6102     button_outline : false,
6103     linkcls : '',
6104     navLink: false,
6105     
6106     getAutoCreate : function(){
6107          
6108         var cfg = {
6109             tag: this.tag,
6110             cls: 'nav-item'
6111         };
6112         
6113         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6114         
6115         if (this.active) {
6116             cfg.cls +=  ' active' ;
6117         }
6118         if (this.disabled) {
6119             cfg.cls += ' disabled';
6120         }
6121         
6122         // BS4 only?
6123         if (this.button_weight.length) {
6124             cfg.tag = this.href ? 'a' : 'button';
6125             cfg.html = this.html || '';
6126             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6127             if (this.href) {
6128                 cfg.href = this.href;
6129             }
6130             if (this.fa) {
6131                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6132             }
6133             
6134             // menu .. should add dropdown-menu class - so no need for carat..
6135             
6136             if (this.badge !== '') {
6137                  
6138                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6139             }
6140             return cfg;
6141         }
6142         
6143         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6144             cfg.cn = [
6145                 {
6146                     tag: this.tagtype,
6147                     href : this.href || "#",
6148                     html: this.html || ''
6149                 }
6150             ];
6151             if (this.tagtype == 'a') {
6152                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6153         
6154             }
6155             if (this.icon) {
6156                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if (this.fa) {
6159                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6160             }
6161             if(this.glyphicon) {
6162                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6163             }
6164             
6165             if (this.menu) {
6166                 
6167                 cfg.cn[0].html += " <span class='caret'></span>";
6168              
6169             }
6170             
6171             if (this.badge !== '') {
6172                  
6173                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6174             }
6175         }
6176         
6177         
6178         
6179         return cfg;
6180     },
6181     onRender : function(ct, position)
6182     {
6183        // Roo.log("Call onRender: " + this.xtype);
6184         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6185             this.tag = 'div';
6186         }
6187         
6188         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6189         this.navLink = this.el.select('.nav-link',true).first();
6190         return ret;
6191     },
6192       
6193     
6194     initEvents: function() 
6195     {
6196         if (typeof (this.menu) != 'undefined') {
6197             this.menu.parentType = this.xtype;
6198             this.menu.triggerEl = this.el;
6199             this.menu = this.addxtype(Roo.apply({}, this.menu));
6200         }
6201         
6202         this.el.on('click', this.onClick, this);
6203         
6204         //if(this.tagtype == 'span'){
6205         //    this.el.select('span',true).on('click', this.onClick, this);
6206         //}
6207        
6208         // at this point parent should be available..
6209         this.parent().register(this);
6210     },
6211     
6212     onClick : function(e)
6213     {
6214         if (e.getTarget('.dropdown-menu-item')) {
6215             // did you click on a menu itemm.... - then don't trigger onclick..
6216             return;
6217         }
6218         
6219         if(
6220                 this.preventDefault || 
6221                 this.href == '#' 
6222         ){
6223             Roo.log("NavItem - prevent Default?");
6224             e.preventDefault();
6225         }
6226         
6227         if (this.disabled) {
6228             return;
6229         }
6230         
6231         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6232         if (tg && tg.transition) {
6233             Roo.log("waiting for the transitionend");
6234             return;
6235         }
6236         
6237         
6238         
6239         //Roo.log("fire event clicked");
6240         if(this.fireEvent('click', this, e) === false){
6241             return;
6242         };
6243         
6244         if(this.tagtype == 'span'){
6245             return;
6246         }
6247         
6248         //Roo.log(this.href);
6249         var ael = this.el.select('a',true).first();
6250         //Roo.log(ael);
6251         
6252         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6253             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6254             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6255                 return; // ignore... - it's a 'hash' to another page.
6256             }
6257             Roo.log("NavItem - prevent Default?");
6258             e.preventDefault();
6259             this.scrollToElement(e);
6260         }
6261         
6262         
6263         var p =  this.parent();
6264    
6265         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6266             if (typeof(p.setActiveItem) !== 'undefined') {
6267                 p.setActiveItem(this);
6268             }
6269         }
6270         
6271         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6272         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6273             // remove the collapsed menu expand...
6274             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6275         }
6276     },
6277     
6278     isActive: function () {
6279         return this.active
6280     },
6281     setActive : function(state, fire, is_was_active)
6282     {
6283         if (this.active && !state && this.navId) {
6284             this.was_active = true;
6285             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6286             if (nv) {
6287                 nv.clearWasActive(this);
6288             }
6289             
6290         }
6291         this.active = state;
6292         
6293         if (!state ) {
6294             this.el.removeClass('active');
6295             this.navLink ? this.navLink.removeClass('active') : false;
6296         } else if (!this.el.hasClass('active')) {
6297             
6298             this.el.addClass('active');
6299             if (Roo.bootstrap.version == 4 && this.navLink ) {
6300                 this.navLink.addClass('active');
6301             }
6302             
6303         }
6304         if (fire) {
6305             this.fireEvent('changed', this, state);
6306         }
6307         
6308         // show a panel if it's registered and related..
6309         
6310         if (!this.navId || !this.tabId || !state || is_was_active) {
6311             return;
6312         }
6313         
6314         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6315         if (!tg) {
6316             return;
6317         }
6318         var pan = tg.getPanelByName(this.tabId);
6319         if (!pan) {
6320             return;
6321         }
6322         // if we can not flip to new panel - go back to old nav highlight..
6323         if (false == tg.showPanel(pan)) {
6324             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6325             if (nv) {
6326                 var onav = nv.getWasActive();
6327                 if (onav) {
6328                     onav.setActive(true, false, true);
6329                 }
6330             }
6331             
6332         }
6333         
6334         
6335         
6336     },
6337      // this should not be here...
6338     setDisabled : function(state)
6339     {
6340         this.disabled = state;
6341         if (!state ) {
6342             this.el.removeClass('disabled');
6343         } else if (!this.el.hasClass('disabled')) {
6344             this.el.addClass('disabled');
6345         }
6346         
6347     },
6348     
6349     /**
6350      * Fetch the element to display the tooltip on.
6351      * @return {Roo.Element} defaults to this.el
6352      */
6353     tooltipEl : function()
6354     {
6355         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6356     },
6357     
6358     scrollToElement : function(e)
6359     {
6360         var c = document.body;
6361         
6362         /*
6363          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6364          */
6365         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6366             c = document.documentElement;
6367         }
6368         
6369         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6370         
6371         if(!target){
6372             return;
6373         }
6374
6375         var o = target.calcOffsetsTo(c);
6376         
6377         var options = {
6378             target : target,
6379             value : o[1]
6380         };
6381         
6382         this.fireEvent('scrollto', this, options, e);
6383         
6384         Roo.get(c).scrollTo('top', options.value, true);
6385         
6386         return;
6387     }
6388 });
6389  
6390
6391  /*
6392  * - LGPL
6393  *
6394  * sidebar item
6395  *
6396  *  li
6397  *    <span> icon </span>
6398  *    <span> text </span>
6399  *    <span>badge </span>
6400  */
6401
6402 /**
6403  * @class Roo.bootstrap.NavSidebarItem
6404  * @extends Roo.bootstrap.NavItem
6405  * Bootstrap Navbar.NavSidebarItem class
6406  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6407  * {Boolean} open is the menu open
6408  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6409  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6410  * {String} buttonSize (sm|md|lg)the extra classes for the button
6411  * {Boolean} showArrow show arrow next to the text (default true)
6412  * @constructor
6413  * Create a new Navbar Button
6414  * @param {Object} config The config object
6415  */
6416 Roo.bootstrap.NavSidebarItem = function(config){
6417     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6418     this.addEvents({
6419         // raw events
6420         /**
6421          * @event click
6422          * The raw click event for the entire grid.
6423          * @param {Roo.EventObject} e
6424          */
6425         "click" : true,
6426          /**
6427             * @event changed
6428             * Fires when the active item active state changes
6429             * @param {Roo.bootstrap.NavSidebarItem} this
6430             * @param {boolean} state the new state
6431              
6432          */
6433         'changed': true
6434     });
6435    
6436 };
6437
6438 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6439     
6440     badgeWeight : 'default',
6441     
6442     open: false,
6443     
6444     buttonView : false,
6445     
6446     buttonWeight : 'default',
6447     
6448     buttonSize : 'md',
6449     
6450     showArrow : true,
6451     
6452     getAutoCreate : function(){
6453         
6454         
6455         var a = {
6456                 tag: 'a',
6457                 href : this.href || '#',
6458                 cls: '',
6459                 html : '',
6460                 cn : []
6461         };
6462         
6463         if(this.buttonView){
6464             a = {
6465                 tag: 'button',
6466                 href : this.href || '#',
6467                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6468                 html : this.html,
6469                 cn : []
6470             };
6471         }
6472         
6473         var cfg = {
6474             tag: 'li',
6475             cls: '',
6476             cn: [ a ]
6477         };
6478         
6479         if (this.active) {
6480             cfg.cls += ' active';
6481         }
6482         
6483         if (this.disabled) {
6484             cfg.cls += ' disabled';
6485         }
6486         if (this.open) {
6487             cfg.cls += ' open x-open';
6488         }
6489         // left icon..
6490         if (this.glyphicon || this.icon) {
6491             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6492             a.cn.push({ tag : 'i', cls : c }) ;
6493         }
6494         
6495         if(!this.buttonView){
6496             var span = {
6497                 tag: 'span',
6498                 html : this.html || ''
6499             };
6500
6501             a.cn.push(span);
6502             
6503         }
6504         
6505         if (this.badge !== '') {
6506             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6507         }
6508         
6509         if (this.menu) {
6510             
6511             if(this.showArrow){
6512                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6513             }
6514             
6515             a.cls += ' dropdown-toggle treeview' ;
6516         }
6517         
6518         return cfg;
6519     },
6520     
6521     initEvents : function()
6522     { 
6523         if (typeof (this.menu) != 'undefined') {
6524             this.menu.parentType = this.xtype;
6525             this.menu.triggerEl = this.el;
6526             this.menu = this.addxtype(Roo.apply({}, this.menu));
6527         }
6528         
6529         this.el.on('click', this.onClick, this);
6530         
6531         if(this.badge !== ''){
6532             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6533         }
6534         
6535     },
6536     
6537     onClick : function(e)
6538     {
6539         if(this.disabled){
6540             e.preventDefault();
6541             return;
6542         }
6543         
6544         if(this.preventDefault){
6545             e.preventDefault();
6546         }
6547         
6548         this.fireEvent('click', this, e);
6549     },
6550     
6551     disable : function()
6552     {
6553         this.setDisabled(true);
6554     },
6555     
6556     enable : function()
6557     {
6558         this.setDisabled(false);
6559     },
6560     
6561     setDisabled : function(state)
6562     {
6563         if(this.disabled == state){
6564             return;
6565         }
6566         
6567         this.disabled = state;
6568         
6569         if (state) {
6570             this.el.addClass('disabled');
6571             return;
6572         }
6573         
6574         this.el.removeClass('disabled');
6575         
6576         return;
6577     },
6578     
6579     setActive : function(state)
6580     {
6581         if(this.active == state){
6582             return;
6583         }
6584         
6585         this.active = state;
6586         
6587         if (state) {
6588             this.el.addClass('active');
6589             return;
6590         }
6591         
6592         this.el.removeClass('active');
6593         
6594         return;
6595     },
6596     
6597     isActive: function () 
6598     {
6599         return this.active;
6600     },
6601     
6602     setBadge : function(str)
6603     {
6604         if(!this.badgeEl){
6605             return;
6606         }
6607         
6608         this.badgeEl.dom.innerHTML = str;
6609     }
6610     
6611    
6612      
6613  
6614 });
6615  
6616
6617  /*
6618  * - LGPL
6619  *
6620  *  Breadcrumb Nav
6621  * 
6622  */
6623 Roo.namespace('Roo.bootstrap.breadcrumb');
6624
6625
6626 /**
6627  * @class Roo.bootstrap.breadcrumb.Nav
6628  * @extends Roo.bootstrap.Component
6629  * Bootstrap Breadcrumb Nav Class
6630  *  
6631  * @children Roo.bootstrap.breadcrumb.Item
6632  * 
6633  * @constructor
6634  * Create a new breadcrumb.Nav
6635  * @param {Object} config The config object
6636  */
6637
6638
6639 Roo.bootstrap.breadcrumb.Nav = function(config){
6640     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6641     
6642     
6643 };
6644
6645 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6646     
6647     getAutoCreate : function()
6648     {
6649
6650         var cfg = {
6651             tag: 'nav',
6652             cn : [
6653                 {
6654                     tag : 'ol',
6655                     cls : 'breadcrumb'
6656                 }
6657             ]
6658             
6659         };
6660           
6661         return cfg;
6662     },
6663     
6664     initEvents: function()
6665     {
6666         this.olEl = this.el.select('ol',true).first();    
6667     },
6668     getChildContainer : function()
6669     {
6670         return this.olEl;  
6671     }
6672     
6673 });
6674
6675  /*
6676  * - LGPL
6677  *
6678  *  Breadcrumb Item
6679  * 
6680  */
6681
6682
6683 /**
6684  * @class Roo.bootstrap.breadcrumb.Nav
6685  * @extends Roo.bootstrap.Component
6686  * Bootstrap Breadcrumb Nav Class
6687  *  
6688  * @children Roo.bootstrap.breadcrumb.Component
6689  * @cfg {String} html the content of the link.
6690  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6691  * @cfg {Boolean} active is it active
6692
6693  * 
6694  * @constructor
6695  * Create a new breadcrumb.Nav
6696  * @param {Object} config The config object
6697  */
6698
6699 Roo.bootstrap.breadcrumb.Item = function(config){
6700     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6701     this.addEvents({
6702         // img events
6703         /**
6704          * @event click
6705          * The img click event for the img.
6706          * @param {Roo.EventObject} e
6707          */
6708         "click" : true
6709     });
6710     
6711 };
6712
6713 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6714     
6715     href: false,
6716     html : '',
6717     
6718     getAutoCreate : function()
6719     {
6720
6721         var cfg = {
6722             tag: 'li',
6723             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6724         };
6725         if (this.href !== false) {
6726             cfg.cn = [{
6727                 tag : 'a',
6728                 href : this.href,
6729                 html : this.html
6730             }];
6731         } else {
6732             cfg.html = this.html;
6733         }
6734         
6735         return cfg;
6736     },
6737     
6738     initEvents: function()
6739     {
6740         if (this.href) {
6741             this.el.select('a', true).first().on('click',this.onClick, this)
6742         }
6743         
6744     },
6745     onClick : function(e)
6746     {
6747         e.preventDefault();
6748         this.fireEvent('click',this,  e);
6749     }
6750     
6751 });
6752
6753  /*
6754  * - LGPL
6755  *
6756  * row
6757  * 
6758  */
6759
6760 /**
6761  * @class Roo.bootstrap.Row
6762  * @extends Roo.bootstrap.Component
6763  * Bootstrap Row class (contains columns...)
6764  * 
6765  * @constructor
6766  * Create a new Row
6767  * @param {Object} config The config object
6768  */
6769
6770 Roo.bootstrap.Row = function(config){
6771     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6772 };
6773
6774 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6775     
6776     getAutoCreate : function(){
6777        return {
6778             cls: 'row clearfix'
6779        };
6780     }
6781     
6782     
6783 });
6784
6785  
6786
6787  /*
6788  * - LGPL
6789  *
6790  * pagination
6791  * 
6792  */
6793
6794 /**
6795  * @class Roo.bootstrap.Pagination
6796  * @extends Roo.bootstrap.Component
6797  * Bootstrap Pagination class
6798  * @cfg {String} size xs | sm | md | lg
6799  * @cfg {Boolean} inverse false | true
6800  * 
6801  * @constructor
6802  * Create a new Pagination
6803  * @param {Object} config The config object
6804  */
6805
6806 Roo.bootstrap.Pagination = function(config){
6807     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6808 };
6809
6810 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6811     
6812     cls: false,
6813     size: false,
6814     inverse: false,
6815     
6816     getAutoCreate : function(){
6817         var cfg = {
6818             tag: 'ul',
6819                 cls: 'pagination'
6820         };
6821         if (this.inverse) {
6822             cfg.cls += ' inverse';
6823         }
6824         if (this.html) {
6825             cfg.html=this.html;
6826         }
6827         if (this.cls) {
6828             cfg.cls += " " + this.cls;
6829         }
6830         return cfg;
6831     }
6832    
6833 });
6834
6835  
6836
6837  /*
6838  * - LGPL
6839  *
6840  * Pagination item
6841  * 
6842  */
6843
6844
6845 /**
6846  * @class Roo.bootstrap.PaginationItem
6847  * @extends Roo.bootstrap.Component
6848  * Bootstrap PaginationItem class
6849  * @cfg {String} html text
6850  * @cfg {String} href the link
6851  * @cfg {Boolean} preventDefault (true | false) default true
6852  * @cfg {Boolean} active (true | false) default false
6853  * @cfg {Boolean} disabled default false
6854  * 
6855  * 
6856  * @constructor
6857  * Create a new PaginationItem
6858  * @param {Object} config The config object
6859  */
6860
6861
6862 Roo.bootstrap.PaginationItem = function(config){
6863     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6864     this.addEvents({
6865         // raw events
6866         /**
6867          * @event click
6868          * The raw click event for the entire grid.
6869          * @param {Roo.EventObject} e
6870          */
6871         "click" : true
6872     });
6873 };
6874
6875 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6876     
6877     href : false,
6878     html : false,
6879     preventDefault: true,
6880     active : false,
6881     cls : false,
6882     disabled: false,
6883     
6884     getAutoCreate : function(){
6885         var cfg= {
6886             tag: 'li',
6887             cn: [
6888                 {
6889                     tag : 'a',
6890                     href : this.href ? this.href : '#',
6891                     html : this.html ? this.html : ''
6892                 }
6893             ]
6894         };
6895         
6896         if(this.cls){
6897             cfg.cls = this.cls;
6898         }
6899         
6900         if(this.disabled){
6901             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6902         }
6903         
6904         if(this.active){
6905             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6906         }
6907         
6908         return cfg;
6909     },
6910     
6911     initEvents: function() {
6912         
6913         this.el.on('click', this.onClick, this);
6914         
6915     },
6916     onClick : function(e)
6917     {
6918         Roo.log('PaginationItem on click ');
6919         if(this.preventDefault){
6920             e.preventDefault();
6921         }
6922         
6923         if(this.disabled){
6924             return;
6925         }
6926         
6927         this.fireEvent('click', this, e);
6928     }
6929    
6930 });
6931
6932  
6933
6934  /*
6935  * - LGPL
6936  *
6937  * slider
6938  * 
6939  */
6940
6941
6942 /**
6943  * @class Roo.bootstrap.Slider
6944  * @extends Roo.bootstrap.Component
6945  * Bootstrap Slider class
6946  *    
6947  * @constructor
6948  * Create a new Slider
6949  * @param {Object} config The config object
6950  */
6951
6952 Roo.bootstrap.Slider = function(config){
6953     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6954 };
6955
6956 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6957     
6958     getAutoCreate : function(){
6959         
6960         var cfg = {
6961             tag: 'div',
6962             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6963             cn: [
6964                 {
6965                     tag: 'a',
6966                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6967                 }
6968             ]
6969         };
6970         
6971         return cfg;
6972     }
6973    
6974 });
6975
6976  /*
6977  * Based on:
6978  * Ext JS Library 1.1.1
6979  * Copyright(c) 2006-2007, Ext JS, LLC.
6980  *
6981  * Originally Released Under LGPL - original licence link has changed is not relivant.
6982  *
6983  * Fork - LGPL
6984  * <script type="text/javascript">
6985  */
6986  
6987
6988 /**
6989  * @class Roo.grid.ColumnModel
6990  * @extends Roo.util.Observable
6991  * This is the default implementation of a ColumnModel used by the Grid. It defines
6992  * the columns in the grid.
6993  * <br>Usage:<br>
6994  <pre><code>
6995  var colModel = new Roo.grid.ColumnModel([
6996         {header: "Ticker", width: 60, sortable: true, locked: true},
6997         {header: "Company Name", width: 150, sortable: true},
6998         {header: "Market Cap.", width: 100, sortable: true},
6999         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7000         {header: "Employees", width: 100, sortable: true, resizable: false}
7001  ]);
7002  </code></pre>
7003  * <p>
7004  
7005  * The config options listed for this class are options which may appear in each
7006  * individual column definition.
7007  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7008  * @constructor
7009  * @param {Object} config An Array of column config objects. See this class's
7010  * config objects for details.
7011 */
7012 Roo.grid.ColumnModel = function(config){
7013         /**
7014      * The config passed into the constructor
7015      */
7016     this.config = config;
7017     this.lookup = {};
7018
7019     // if no id, create one
7020     // if the column does not have a dataIndex mapping,
7021     // map it to the order it is in the config
7022     for(var i = 0, len = config.length; i < len; i++){
7023         var c = config[i];
7024         if(typeof c.dataIndex == "undefined"){
7025             c.dataIndex = i;
7026         }
7027         if(typeof c.renderer == "string"){
7028             c.renderer = Roo.util.Format[c.renderer];
7029         }
7030         if(typeof c.id == "undefined"){
7031             c.id = Roo.id();
7032         }
7033         if(c.editor && c.editor.xtype){
7034             c.editor  = Roo.factory(c.editor, Roo.grid);
7035         }
7036         if(c.editor && c.editor.isFormField){
7037             c.editor = new Roo.grid.GridEditor(c.editor);
7038         }
7039         this.lookup[c.id] = c;
7040     }
7041
7042     /**
7043      * The width of columns which have no width specified (defaults to 100)
7044      * @type Number
7045      */
7046     this.defaultWidth = 100;
7047
7048     /**
7049      * Default sortable of columns which have no sortable specified (defaults to false)
7050      * @type Boolean
7051      */
7052     this.defaultSortable = false;
7053
7054     this.addEvents({
7055         /**
7056              * @event widthchange
7057              * Fires when the width of a column changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newWidth The new width
7061              */
7062             "widthchange": true,
7063         /**
7064              * @event headerchange
7065              * Fires when the text of a header changes.
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Number} newText The new header text
7069              */
7070             "headerchange": true,
7071         /**
7072              * @event hiddenchange
7073              * Fires when a column is hidden or "unhidden".
7074              * @param {ColumnModel} this
7075              * @param {Number} columnIndex The column index
7076              * @param {Boolean} hidden true if hidden, false otherwise
7077              */
7078             "hiddenchange": true,
7079             /**
7080          * @event columnmoved
7081          * Fires when a column is moved.
7082          * @param {ColumnModel} this
7083          * @param {Number} oldIndex
7084          * @param {Number} newIndex
7085          */
7086         "columnmoved" : true,
7087         /**
7088          * @event columlockchange
7089          * Fires when a column's locked state is changed
7090          * @param {ColumnModel} this
7091          * @param {Number} colIndex
7092          * @param {Boolean} locked true if locked
7093          */
7094         "columnlockchange" : true
7095     });
7096     Roo.grid.ColumnModel.superclass.constructor.call(this);
7097 };
7098 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7099     /**
7100      * @cfg {String} header The header text to display in the Grid view.
7101      */
7102     /**
7103      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7104      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7105      * specified, the column's index is used as an index into the Record's data Array.
7106      */
7107     /**
7108      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7109      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7110      */
7111     /**
7112      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7113      * Defaults to the value of the {@link #defaultSortable} property.
7114      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7115      */
7116     /**
7117      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7121      */
7122     /**
7123      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7124      */
7125     /**
7126      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7127      */
7128     /**
7129      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7130      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7131      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7132      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7133      */
7134        /**
7135      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7136      */
7137     /**
7138      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7142      */
7143     /**
7144      * @cfg {String} cursor (Optional)
7145      */
7146     /**
7147      * @cfg {String} tooltip (Optional)
7148      */
7149     /**
7150      * @cfg {Number} xs (Optional)
7151      */
7152     /**
7153      * @cfg {Number} sm (Optional)
7154      */
7155     /**
7156      * @cfg {Number} md (Optional)
7157      */
7158     /**
7159      * @cfg {Number} lg (Optional)
7160      */
7161     /**
7162      * Returns the id of the column at the specified index.
7163      * @param {Number} index The column index
7164      * @return {String} the id
7165      */
7166     getColumnId : function(index){
7167         return this.config[index].id;
7168     },
7169
7170     /**
7171      * Returns the column for a specified id.
7172      * @param {String} id The column id
7173      * @return {Object} the column
7174      */
7175     getColumnById : function(id){
7176         return this.lookup[id];
7177     },
7178
7179     
7180     /**
7181      * Returns the column for a specified dataIndex.
7182      * @param {String} dataIndex The column dataIndex
7183      * @return {Object|Boolean} the column or false if not found
7184      */
7185     getColumnByDataIndex: function(dataIndex){
7186         var index = this.findColumnIndex(dataIndex);
7187         return index > -1 ? this.config[index] : false;
7188     },
7189     
7190     /**
7191      * Returns the index for a specified column id.
7192      * @param {String} id The column id
7193      * @return {Number} the index, or -1 if not found
7194      */
7195     getIndexById : function(id){
7196         for(var i = 0, len = this.config.length; i < len; i++){
7197             if(this.config[i].id == id){
7198                 return i;
7199             }
7200         }
7201         return -1;
7202     },
7203     
7204     /**
7205      * Returns the index for a specified column dataIndex.
7206      * @param {String} dataIndex The column dataIndex
7207      * @return {Number} the index, or -1 if not found
7208      */
7209     
7210     findColumnIndex : function(dataIndex){
7211         for(var i = 0, len = this.config.length; i < len; i++){
7212             if(this.config[i].dataIndex == dataIndex){
7213                 return i;
7214             }
7215         }
7216         return -1;
7217     },
7218     
7219     
7220     moveColumn : function(oldIndex, newIndex){
7221         var c = this.config[oldIndex];
7222         this.config.splice(oldIndex, 1);
7223         this.config.splice(newIndex, 0, c);
7224         this.dataMap = null;
7225         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7226     },
7227
7228     isLocked : function(colIndex){
7229         return this.config[colIndex].locked === true;
7230     },
7231
7232     setLocked : function(colIndex, value, suppressEvent){
7233         if(this.isLocked(colIndex) == value){
7234             return;
7235         }
7236         this.config[colIndex].locked = value;
7237         if(!suppressEvent){
7238             this.fireEvent("columnlockchange", this, colIndex, value);
7239         }
7240     },
7241
7242     getTotalLockedWidth : function(){
7243         var totalWidth = 0;
7244         for(var i = 0; i < this.config.length; i++){
7245             if(this.isLocked(i) && !this.isHidden(i)){
7246                 this.totalWidth += this.getColumnWidth(i);
7247             }
7248         }
7249         return totalWidth;
7250     },
7251
7252     getLockedCount : function(){
7253         for(var i = 0, len = this.config.length; i < len; i++){
7254             if(!this.isLocked(i)){
7255                 return i;
7256             }
7257         }
7258         
7259         return this.config.length;
7260     },
7261
7262     /**
7263      * Returns the number of columns.
7264      * @return {Number}
7265      */
7266     getColumnCount : function(visibleOnly){
7267         if(visibleOnly === true){
7268             var c = 0;
7269             for(var i = 0, len = this.config.length; i < len; i++){
7270                 if(!this.isHidden(i)){
7271                     c++;
7272                 }
7273             }
7274             return c;
7275         }
7276         return this.config.length;
7277     },
7278
7279     /**
7280      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7281      * @param {Function} fn
7282      * @param {Object} scope (optional)
7283      * @return {Array} result
7284      */
7285     getColumnsBy : function(fn, scope){
7286         var r = [];
7287         for(var i = 0, len = this.config.length; i < len; i++){
7288             var c = this.config[i];
7289             if(fn.call(scope||this, c, i) === true){
7290                 r[r.length] = c;
7291             }
7292         }
7293         return r;
7294     },
7295
7296     /**
7297      * Returns true if the specified column is sortable.
7298      * @param {Number} col The column index
7299      * @return {Boolean}
7300      */
7301     isSortable : function(col){
7302         if(typeof this.config[col].sortable == "undefined"){
7303             return this.defaultSortable;
7304         }
7305         return this.config[col].sortable;
7306     },
7307
7308     /**
7309      * Returns the rendering (formatting) function defined for the column.
7310      * @param {Number} col The column index.
7311      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7312      */
7313     getRenderer : function(col){
7314         if(!this.config[col].renderer){
7315             return Roo.grid.ColumnModel.defaultRenderer;
7316         }
7317         return this.config[col].renderer;
7318     },
7319
7320     /**
7321      * Sets the rendering (formatting) function for a column.
7322      * @param {Number} col The column index
7323      * @param {Function} fn The function to use to process the cell's raw data
7324      * to return HTML markup for the grid view. The render function is called with
7325      * the following parameters:<ul>
7326      * <li>Data value.</li>
7327      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7328      * <li>css A CSS style string to apply to the table cell.</li>
7329      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7330      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7331      * <li>Row index</li>
7332      * <li>Column index</li>
7333      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7334      */
7335     setRenderer : function(col, fn){
7336         this.config[col].renderer = fn;
7337     },
7338
7339     /**
7340      * Returns the width for the specified column.
7341      * @param {Number} col The column index
7342      * @return {Number}
7343      */
7344     getColumnWidth : function(col){
7345         return this.config[col].width * 1 || this.defaultWidth;
7346     },
7347
7348     /**
7349      * Sets the width for a column.
7350      * @param {Number} col The column index
7351      * @param {Number} width The new width
7352      */
7353     setColumnWidth : function(col, width, suppressEvent){
7354         this.config[col].width = width;
7355         this.totalWidth = null;
7356         if(!suppressEvent){
7357              this.fireEvent("widthchange", this, col, width);
7358         }
7359     },
7360
7361     /**
7362      * Returns the total width of all columns.
7363      * @param {Boolean} includeHidden True to include hidden column widths
7364      * @return {Number}
7365      */
7366     getTotalWidth : function(includeHidden){
7367         if(!this.totalWidth){
7368             this.totalWidth = 0;
7369             for(var i = 0, len = this.config.length; i < len; i++){
7370                 if(includeHidden || !this.isHidden(i)){
7371                     this.totalWidth += this.getColumnWidth(i);
7372                 }
7373             }
7374         }
7375         return this.totalWidth;
7376     },
7377
7378     /**
7379      * Returns the header for the specified column.
7380      * @param {Number} col The column index
7381      * @return {String}
7382      */
7383     getColumnHeader : function(col){
7384         return this.config[col].header;
7385     },
7386
7387     /**
7388      * Sets the header for a column.
7389      * @param {Number} col The column index
7390      * @param {String} header The new header
7391      */
7392     setColumnHeader : function(col, header){
7393         this.config[col].header = header;
7394         this.fireEvent("headerchange", this, col, header);
7395     },
7396
7397     /**
7398      * Returns the tooltip for the specified column.
7399      * @param {Number} col The column index
7400      * @return {String}
7401      */
7402     getColumnTooltip : function(col){
7403             return this.config[col].tooltip;
7404     },
7405     /**
7406      * Sets the tooltip for a column.
7407      * @param {Number} col The column index
7408      * @param {String} tooltip The new tooltip
7409      */
7410     setColumnTooltip : function(col, tooltip){
7411             this.config[col].tooltip = tooltip;
7412     },
7413
7414     /**
7415      * Returns the dataIndex for the specified column.
7416      * @param {Number} col The column index
7417      * @return {Number}
7418      */
7419     getDataIndex : function(col){
7420         return this.config[col].dataIndex;
7421     },
7422
7423     /**
7424      * Sets the dataIndex for a column.
7425      * @param {Number} col The column index
7426      * @param {Number} dataIndex The new dataIndex
7427      */
7428     setDataIndex : function(col, dataIndex){
7429         this.config[col].dataIndex = dataIndex;
7430     },
7431
7432     
7433     
7434     /**
7435      * Returns true if the cell is editable.
7436      * @param {Number} colIndex The column index
7437      * @param {Number} rowIndex The row index - this is nto actually used..?
7438      * @return {Boolean}
7439      */
7440     isCellEditable : function(colIndex, rowIndex){
7441         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7442     },
7443
7444     /**
7445      * Returns the editor defined for the cell/column.
7446      * return false or null to disable editing.
7447      * @param {Number} colIndex The column index
7448      * @param {Number} rowIndex The row index
7449      * @return {Object}
7450      */
7451     getCellEditor : function(colIndex, rowIndex){
7452         return this.config[colIndex].editor;
7453     },
7454
7455     /**
7456      * Sets if a column is editable.
7457      * @param {Number} col The column index
7458      * @param {Boolean} editable True if the column is editable
7459      */
7460     setEditable : function(col, editable){
7461         this.config[col].editable = editable;
7462     },
7463
7464
7465     /**
7466      * Returns true if the column is hidden.
7467      * @param {Number} colIndex The column index
7468      * @return {Boolean}
7469      */
7470     isHidden : function(colIndex){
7471         return this.config[colIndex].hidden;
7472     },
7473
7474
7475     /**
7476      * Returns true if the column width cannot be changed
7477      */
7478     isFixed : function(colIndex){
7479         return this.config[colIndex].fixed;
7480     },
7481
7482     /**
7483      * Returns true if the column can be resized
7484      * @return {Boolean}
7485      */
7486     isResizable : function(colIndex){
7487         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7488     },
7489     /**
7490      * Sets if a column is hidden.
7491      * @param {Number} colIndex The column index
7492      * @param {Boolean} hidden True if the column is hidden
7493      */
7494     setHidden : function(colIndex, hidden){
7495         this.config[colIndex].hidden = hidden;
7496         this.totalWidth = null;
7497         this.fireEvent("hiddenchange", this, colIndex, hidden);
7498     },
7499
7500     /**
7501      * Sets the editor for a column.
7502      * @param {Number} col The column index
7503      * @param {Object} editor The editor object
7504      */
7505     setEditor : function(col, editor){
7506         this.config[col].editor = editor;
7507     }
7508 });
7509
7510 Roo.grid.ColumnModel.defaultRenderer = function(value)
7511 {
7512     if(typeof value == "object") {
7513         return value;
7514     }
7515         if(typeof value == "string" && value.length < 1){
7516             return "&#160;";
7517         }
7518     
7519         return String.format("{0}", value);
7520 };
7521
7522 // Alias for backwards compatibility
7523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7524 /*
7525  * Based on:
7526  * Ext JS Library 1.1.1
7527  * Copyright(c) 2006-2007, Ext JS, LLC.
7528  *
7529  * Originally Released Under LGPL - original licence link has changed is not relivant.
7530  *
7531  * Fork - LGPL
7532  * <script type="text/javascript">
7533  */
7534  
7535 /**
7536  * @class Roo.LoadMask
7537  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7538  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7539  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7540  * element's UpdateManager load indicator and will be destroyed after the initial load.
7541  * @constructor
7542  * Create a new LoadMask
7543  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7544  * @param {Object} config The config object
7545  */
7546 Roo.LoadMask = function(el, config){
7547     this.el = Roo.get(el);
7548     Roo.apply(this, config);
7549     if(this.store){
7550         this.store.on('beforeload', this.onBeforeLoad, this);
7551         this.store.on('load', this.onLoad, this);
7552         this.store.on('loadexception', this.onLoadException, this);
7553         this.removeMask = false;
7554     }else{
7555         var um = this.el.getUpdateManager();
7556         um.showLoadIndicator = false; // disable the default indicator
7557         um.on('beforeupdate', this.onBeforeLoad, this);
7558         um.on('update', this.onLoad, this);
7559         um.on('failure', this.onLoad, this);
7560         this.removeMask = true;
7561     }
7562 };
7563
7564 Roo.LoadMask.prototype = {
7565     /**
7566      * @cfg {Boolean} removeMask
7567      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7568      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7569      */
7570     /**
7571      * @cfg {String} msg
7572      * The text to display in a centered loading message box (defaults to 'Loading...')
7573      */
7574     msg : 'Loading...',
7575     /**
7576      * @cfg {String} msgCls
7577      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7578      */
7579     msgCls : 'x-mask-loading',
7580
7581     /**
7582      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7583      * @type Boolean
7584      */
7585     disabled: false,
7586
7587     /**
7588      * Disables the mask to prevent it from being displayed
7589      */
7590     disable : function(){
7591        this.disabled = true;
7592     },
7593
7594     /**
7595      * Enables the mask so that it can be displayed
7596      */
7597     enable : function(){
7598         this.disabled = false;
7599     },
7600     
7601     onLoadException : function()
7602     {
7603         Roo.log(arguments);
7604         
7605         if (typeof(arguments[3]) != 'undefined') {
7606             Roo.MessageBox.alert("Error loading",arguments[3]);
7607         } 
7608         /*
7609         try {
7610             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7611                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7612             }   
7613         } catch(e) {
7614             
7615         }
7616         */
7617     
7618         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7619     },
7620     // private
7621     onLoad : function()
7622     {
7623         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7624     },
7625
7626     // private
7627     onBeforeLoad : function(){
7628         if(!this.disabled){
7629             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7630         }
7631     },
7632
7633     // private
7634     destroy : function(){
7635         if(this.store){
7636             this.store.un('beforeload', this.onBeforeLoad, this);
7637             this.store.un('load', this.onLoad, this);
7638             this.store.un('loadexception', this.onLoadException, this);
7639         }else{
7640             var um = this.el.getUpdateManager();
7641             um.un('beforeupdate', this.onBeforeLoad, this);
7642             um.un('update', this.onLoad, this);
7643             um.un('failure', this.onLoad, this);
7644         }
7645     }
7646 };/*
7647  * - LGPL
7648  *
7649  * table
7650  * 
7651  */
7652
7653 /**
7654  * @class Roo.bootstrap.Table
7655  * @extends Roo.bootstrap.Component
7656  * Bootstrap Table class
7657  * @cfg {String} cls table class
7658  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7659  * @cfg {String} bgcolor Specifies the background color for a table
7660  * @cfg {Number} border Specifies whether the table cells should have borders or not
7661  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7662  * @cfg {Number} cellspacing Specifies the space between cells
7663  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7664  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7665  * @cfg {String} sortable Specifies that the table should be sortable
7666  * @cfg {String} summary Specifies a summary of the content of a table
7667  * @cfg {Number} width Specifies the width of a table
7668  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7669  * 
7670  * @cfg {boolean} striped Should the rows be alternative striped
7671  * @cfg {boolean} bordered Add borders to the table
7672  * @cfg {boolean} hover Add hover highlighting
7673  * @cfg {boolean} condensed Format condensed
7674  * @cfg {boolean} responsive Format condensed
7675  * @cfg {Boolean} loadMask (true|false) default false
7676  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7677  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7678  * @cfg {Boolean} rowSelection (true|false) default false
7679  * @cfg {Boolean} cellSelection (true|false) default false
7680  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7681  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7682  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7683  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7684  
7685  * 
7686  * @constructor
7687  * Create a new Table
7688  * @param {Object} config The config object
7689  */
7690
7691 Roo.bootstrap.Table = function(config){
7692     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7693     
7694   
7695     
7696     // BC...
7697     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7698     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7699     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7700     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7701     
7702     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7703     if (this.sm) {
7704         this.sm.grid = this;
7705         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7706         this.sm = this.selModel;
7707         this.sm.xmodule = this.xmodule || false;
7708     }
7709     
7710     if (this.cm && typeof(this.cm.config) == 'undefined') {
7711         this.colModel = new Roo.grid.ColumnModel(this.cm);
7712         this.cm = this.colModel;
7713         this.cm.xmodule = this.xmodule || false;
7714     }
7715     if (this.store) {
7716         this.store= Roo.factory(this.store, Roo.data);
7717         this.ds = this.store;
7718         this.ds.xmodule = this.xmodule || false;
7719          
7720     }
7721     if (this.footer && this.store) {
7722         this.footer.dataSource = this.ds;
7723         this.footer = Roo.factory(this.footer);
7724     }
7725     
7726     /** @private */
7727     this.addEvents({
7728         /**
7729          * @event cellclick
7730          * Fires when a cell is clicked
7731          * @param {Roo.bootstrap.Table} this
7732          * @param {Roo.Element} el
7733          * @param {Number} rowIndex
7734          * @param {Number} columnIndex
7735          * @param {Roo.EventObject} e
7736          */
7737         "cellclick" : true,
7738         /**
7739          * @event celldblclick
7740          * Fires when a cell is double clicked
7741          * @param {Roo.bootstrap.Table} this
7742          * @param {Roo.Element} el
7743          * @param {Number} rowIndex
7744          * @param {Number} columnIndex
7745          * @param {Roo.EventObject} e
7746          */
7747         "celldblclick" : true,
7748         /**
7749          * @event rowclick
7750          * Fires when a row is clicked
7751          * @param {Roo.bootstrap.Table} this
7752          * @param {Roo.Element} el
7753          * @param {Number} rowIndex
7754          * @param {Roo.EventObject} e
7755          */
7756         "rowclick" : true,
7757         /**
7758          * @event rowdblclick
7759          * Fires when a row is double clicked
7760          * @param {Roo.bootstrap.Table} this
7761          * @param {Roo.Element} el
7762          * @param {Number} rowIndex
7763          * @param {Roo.EventObject} e
7764          */
7765         "rowdblclick" : true,
7766         /**
7767          * @event mouseover
7768          * Fires when a mouseover occur
7769          * @param {Roo.bootstrap.Table} this
7770          * @param {Roo.Element} el
7771          * @param {Number} rowIndex
7772          * @param {Number} columnIndex
7773          * @param {Roo.EventObject} e
7774          */
7775         "mouseover" : true,
7776         /**
7777          * @event mouseout
7778          * Fires when a mouseout occur
7779          * @param {Roo.bootstrap.Table} this
7780          * @param {Roo.Element} el
7781          * @param {Number} rowIndex
7782          * @param {Number} columnIndex
7783          * @param {Roo.EventObject} e
7784          */
7785         "mouseout" : true,
7786         /**
7787          * @event rowclass
7788          * Fires when a row is rendered, so you can change add a style to it.
7789          * @param {Roo.bootstrap.Table} this
7790          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7791          */
7792         'rowclass' : true,
7793           /**
7794          * @event rowsrendered
7795          * Fires when all the  rows have been rendered
7796          * @param {Roo.bootstrap.Table} this
7797          */
7798         'rowsrendered' : true,
7799         /**
7800          * @event contextmenu
7801          * The raw contextmenu event for the entire grid.
7802          * @param {Roo.EventObject} e
7803          */
7804         "contextmenu" : true,
7805         /**
7806          * @event rowcontextmenu
7807          * Fires when a row is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Roo.EventObject} e
7811          */
7812         "rowcontextmenu" : true,
7813         /**
7814          * @event cellcontextmenu
7815          * Fires when a cell is right clicked
7816          * @param {Roo.bootstrap.Table} this
7817          * @param {Number} rowIndex
7818          * @param {Number} cellIndex
7819          * @param {Roo.EventObject} e
7820          */
7821          "cellcontextmenu" : true,
7822          /**
7823          * @event headercontextmenu
7824          * Fires when a header is right clicked
7825          * @param {Roo.bootstrap.Table} this
7826          * @param {Number} columnIndex
7827          * @param {Roo.EventObject} e
7828          */
7829         "headercontextmenu" : true
7830     });
7831 };
7832
7833 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7834     
7835     cls: false,
7836     align: false,
7837     bgcolor: false,
7838     border: false,
7839     cellpadding: false,
7840     cellspacing: false,
7841     frame: false,
7842     rules: false,
7843     sortable: false,
7844     summary: false,
7845     width: false,
7846     striped : false,
7847     scrollBody : false,
7848     bordered: false,
7849     hover:  false,
7850     condensed : false,
7851     responsive : false,
7852     sm : false,
7853     cm : false,
7854     store : false,
7855     loadMask : false,
7856     footerShow : true,
7857     headerShow : true,
7858   
7859     rowSelection : false,
7860     cellSelection : false,
7861     layout : false,
7862     
7863     // Roo.Element - the tbody
7864     mainBody: false,
7865     // Roo.Element - thead element
7866     mainHead: false,
7867     
7868     container: false, // used by gridpanel...
7869     
7870     lazyLoad : false,
7871     
7872     CSS : Roo.util.CSS,
7873     
7874     auto_hide_footer : false,
7875     
7876     getAutoCreate : function()
7877     {
7878         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7879         
7880         cfg = {
7881             tag: 'table',
7882             cls : 'table',
7883             cn : []
7884         };
7885         if (this.scrollBody) {
7886             cfg.cls += ' table-body-fixed';
7887         }    
7888         if (this.striped) {
7889             cfg.cls += ' table-striped';
7890         }
7891         
7892         if (this.hover) {
7893             cfg.cls += ' table-hover';
7894         }
7895         if (this.bordered) {
7896             cfg.cls += ' table-bordered';
7897         }
7898         if (this.condensed) {
7899             cfg.cls += ' table-condensed';
7900         }
7901         if (this.responsive) {
7902             cfg.cls += ' table-responsive';
7903         }
7904         
7905         if (this.cls) {
7906             cfg.cls+=  ' ' +this.cls;
7907         }
7908         
7909         // this lot should be simplifed...
7910         var _t = this;
7911         var cp = [
7912             'align',
7913             'bgcolor',
7914             'border',
7915             'cellpadding',
7916             'cellspacing',
7917             'frame',
7918             'rules',
7919             'sortable',
7920             'summary',
7921             'width'
7922         ].forEach(function(k) {
7923             if (_t[k]) {
7924                 cfg[k] = _t[k];
7925             }
7926         });
7927         
7928         
7929         if (this.layout) {
7930             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7931         }
7932         
7933         if(this.store || this.cm){
7934             if(this.headerShow){
7935                 cfg.cn.push(this.renderHeader());
7936             }
7937             
7938             cfg.cn.push(this.renderBody());
7939             
7940             if(this.footerShow){
7941                 cfg.cn.push(this.renderFooter());
7942             }
7943             // where does this come from?
7944             //cfg.cls+=  ' TableGrid';
7945         }
7946         
7947         return { cn : [ cfg ] };
7948     },
7949     
7950     initEvents : function()
7951     {   
7952         if(!this.store || !this.cm){
7953             return;
7954         }
7955         if (this.selModel) {
7956             this.selModel.initEvents();
7957         }
7958         
7959         
7960         //Roo.log('initEvents with ds!!!!');
7961         
7962         this.mainBody = this.el.select('tbody', true).first();
7963         this.mainHead = this.el.select('thead', true).first();
7964         this.mainFoot = this.el.select('tfoot', true).first();
7965         
7966         
7967         
7968         var _this = this;
7969         
7970         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7971             e.on('click', _this.sort, _this);
7972         });
7973         
7974         this.mainBody.on("click", this.onClick, this);
7975         this.mainBody.on("dblclick", this.onDblClick, this);
7976         
7977         // why is this done????? = it breaks dialogs??
7978         //this.parent().el.setStyle('position', 'relative');
7979         
7980         
7981         if (this.footer) {
7982             this.footer.parentId = this.id;
7983             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7984             
7985             if(this.lazyLoad){
7986                 this.el.select('tfoot tr td').first().addClass('hide');
7987             }
7988         } 
7989         
7990         if(this.loadMask) {
7991             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7992         }
7993         
7994         this.store.on('load', this.onLoad, this);
7995         this.store.on('beforeload', this.onBeforeLoad, this);
7996         this.store.on('update', this.onUpdate, this);
7997         this.store.on('add', this.onAdd, this);
7998         this.store.on("clear", this.clear, this);
7999         
8000         this.el.on("contextmenu", this.onContextMenu, this);
8001         
8002         this.mainBody.on('scroll', this.onBodyScroll, this);
8003         
8004         this.cm.on("headerchange", this.onHeaderChange, this);
8005         
8006         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8007         
8008     },
8009     
8010     onContextMenu : function(e, t)
8011     {
8012         this.processEvent("contextmenu", e);
8013     },
8014     
8015     processEvent : function(name, e)
8016     {
8017         if (name != 'touchstart' ) {
8018             this.fireEvent(name, e);    
8019         }
8020         
8021         var t = e.getTarget();
8022         
8023         var cell = Roo.get(t);
8024         
8025         if(!cell){
8026             return;
8027         }
8028         
8029         if(cell.findParent('tfoot', false, true)){
8030             return;
8031         }
8032         
8033         if(cell.findParent('thead', false, true)){
8034             
8035             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8036                 cell = Roo.get(t).findParent('th', false, true);
8037                 if (!cell) {
8038                     Roo.log("failed to find th in thead?");
8039                     Roo.log(e.getTarget());
8040                     return;
8041                 }
8042             }
8043             
8044             var cellIndex = cell.dom.cellIndex;
8045             
8046             var ename = name == 'touchstart' ? 'click' : name;
8047             this.fireEvent("header" + ename, this, cellIndex, e);
8048             
8049             return;
8050         }
8051         
8052         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8053             cell = Roo.get(t).findParent('td', false, true);
8054             if (!cell) {
8055                 Roo.log("failed to find th in tbody?");
8056                 Roo.log(e.getTarget());
8057                 return;
8058             }
8059         }
8060         
8061         var row = cell.findParent('tr', false, true);
8062         var cellIndex = cell.dom.cellIndex;
8063         var rowIndex = row.dom.rowIndex - 1;
8064         
8065         if(row !== false){
8066             
8067             this.fireEvent("row" + name, this, rowIndex, e);
8068             
8069             if(cell !== false){
8070             
8071                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8072             }
8073         }
8074         
8075     },
8076     
8077     onMouseover : function(e, el)
8078     {
8079         var cell = Roo.get(el);
8080         
8081         if(!cell){
8082             return;
8083         }
8084         
8085         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8086             cell = cell.findParent('td', false, true);
8087         }
8088         
8089         var row = cell.findParent('tr', false, true);
8090         var cellIndex = cell.dom.cellIndex;
8091         var rowIndex = row.dom.rowIndex - 1; // start from 0
8092         
8093         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8094         
8095     },
8096     
8097     onMouseout : function(e, el)
8098     {
8099         var cell = Roo.get(el);
8100         
8101         if(!cell){
8102             return;
8103         }
8104         
8105         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8106             cell = cell.findParent('td', false, true);
8107         }
8108         
8109         var row = cell.findParent('tr', false, true);
8110         var cellIndex = cell.dom.cellIndex;
8111         var rowIndex = row.dom.rowIndex - 1; // start from 0
8112         
8113         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8114         
8115     },
8116     
8117     onClick : function(e, el)
8118     {
8119         var cell = Roo.get(el);
8120         
8121         if(!cell || (!this.cellSelection && !this.rowSelection)){
8122             return;
8123         }
8124         
8125         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8126             cell = cell.findParent('td', false, true);
8127         }
8128         
8129         if(!cell || typeof(cell) == 'undefined'){
8130             return;
8131         }
8132         
8133         var row = cell.findParent('tr', false, true);
8134         
8135         if(!row || typeof(row) == 'undefined'){
8136             return;
8137         }
8138         
8139         var cellIndex = cell.dom.cellIndex;
8140         var rowIndex = this.getRowIndex(row);
8141         
8142         // why??? - should these not be based on SelectionModel?
8143         if(this.cellSelection){
8144             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8145         }
8146         
8147         if(this.rowSelection){
8148             this.fireEvent('rowclick', this, row, rowIndex, e);
8149         }
8150         
8151         
8152     },
8153         
8154     onDblClick : function(e,el)
8155     {
8156         var cell = Roo.get(el);
8157         
8158         if(!cell || (!this.cellSelection && !this.rowSelection)){
8159             return;
8160         }
8161         
8162         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8163             cell = cell.findParent('td', false, true);
8164         }
8165         
8166         if(!cell || typeof(cell) == 'undefined'){
8167             return;
8168         }
8169         
8170         var row = cell.findParent('tr', false, true);
8171         
8172         if(!row || typeof(row) == 'undefined'){
8173             return;
8174         }
8175         
8176         var cellIndex = cell.dom.cellIndex;
8177         var rowIndex = this.getRowIndex(row);
8178         
8179         if(this.cellSelection){
8180             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8181         }
8182         
8183         if(this.rowSelection){
8184             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8185         }
8186     },
8187     
8188     sort : function(e,el)
8189     {
8190         var col = Roo.get(el);
8191         
8192         if(!col.hasClass('sortable')){
8193             return;
8194         }
8195         
8196         var sort = col.attr('sort');
8197         var dir = 'ASC';
8198         
8199         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8200             dir = 'DESC';
8201         }
8202         
8203         this.store.sortInfo = {field : sort, direction : dir};
8204         
8205         if (this.footer) {
8206             Roo.log("calling footer first");
8207             this.footer.onClick('first');
8208         } else {
8209         
8210             this.store.load({ params : { start : 0 } });
8211         }
8212     },
8213     
8214     renderHeader : function()
8215     {
8216         var header = {
8217             tag: 'thead',
8218             cn : []
8219         };
8220         
8221         var cm = this.cm;
8222         this.totalWidth = 0;
8223         
8224         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8225             
8226             var config = cm.config[i];
8227             
8228             var c = {
8229                 tag: 'th',
8230                 cls : 'x-hcol-' + i,
8231                 style : '',
8232                 html: cm.getColumnHeader(i)
8233             };
8234             
8235             var hh = '';
8236             
8237             if(typeof(config.sortable) != 'undefined' && config.sortable){
8238                 c.cls = 'sortable';
8239                 c.html = '<i class="glyphicon"></i>' + c.html;
8240             }
8241             
8242             // could use BS4 hidden-..-down 
8243             
8244             if(typeof(config.lgHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.mdHeader) != 'undefined'){
8249                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8250             }
8251             
8252             if(typeof(config.smHeader) != 'undefined'){
8253                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8254             }
8255             
8256             if(typeof(config.xsHeader) != 'undefined'){
8257                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8258             }
8259             
8260             if(hh.length){
8261                 c.html = hh;
8262             }
8263             
8264             if(typeof(config.tooltip) != 'undefined'){
8265                 c.tooltip = config.tooltip;
8266             }
8267             
8268             if(typeof(config.colspan) != 'undefined'){
8269                 c.colspan = config.colspan;
8270             }
8271             
8272             if(typeof(config.hidden) != 'undefined' && config.hidden){
8273                 c.style += ' display:none;';
8274             }
8275             
8276             if(typeof(config.dataIndex) != 'undefined'){
8277                 c.sort = config.dataIndex;
8278             }
8279             
8280            
8281             
8282             if(typeof(config.align) != 'undefined' && config.align.length){
8283                 c.style += ' text-align:' + config.align + ';';
8284             }
8285             
8286             if(typeof(config.width) != 'undefined'){
8287                 c.style += ' width:' + config.width + 'px;';
8288                 this.totalWidth += config.width;
8289             } else {
8290                 this.totalWidth += 100; // assume minimum of 100 per column?
8291             }
8292             
8293             if(typeof(config.cls) != 'undefined'){
8294                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8295             }
8296             
8297             ['xs','sm','md','lg'].map(function(size){
8298                 
8299                 if(typeof(config[size]) == 'undefined'){
8300                     return;
8301                 }
8302                  
8303                 if (!config[size]) { // 0 = hidden
8304                     // BS 4 '0' is treated as hide that column and below.
8305                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8306                     return;
8307                 }
8308                 
8309                 c.cls += ' col-' + size + '-' + config[size] + (
8310                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8311                 );
8312                 
8313                 
8314             });
8315             
8316             header.cn.push(c)
8317         }
8318         
8319         return header;
8320     },
8321     
8322     renderBody : function()
8323     {
8324         var body = {
8325             tag: 'tbody',
8326             cn : [
8327                 {
8328                     tag: 'tr',
8329                     cn : [
8330                         {
8331                             tag : 'td',
8332                             colspan :  this.cm.getColumnCount()
8333                         }
8334                     ]
8335                 }
8336             ]
8337         };
8338         
8339         return body;
8340     },
8341     
8342     renderFooter : function()
8343     {
8344         var footer = {
8345             tag: 'tfoot',
8346             cn : [
8347                 {
8348                     tag: 'tr',
8349                     cn : [
8350                         {
8351                             tag : 'td',
8352                             colspan :  this.cm.getColumnCount()
8353                         }
8354                     ]
8355                 }
8356             ]
8357         };
8358         
8359         return footer;
8360     },
8361     
8362     
8363     
8364     onLoad : function()
8365     {
8366 //        Roo.log('ds onload');
8367         this.clear();
8368         
8369         var _this = this;
8370         var cm = this.cm;
8371         var ds = this.store;
8372         
8373         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8374             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8375             if (_this.store.sortInfo) {
8376                     
8377                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8378                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8379                 }
8380                 
8381                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8382                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8383                 }
8384             }
8385         });
8386         
8387         var tbody =  this.mainBody;
8388               
8389         if(ds.getCount() > 0){
8390             ds.data.each(function(d,rowIndex){
8391                 var row =  this.renderRow(cm, ds, rowIndex);
8392                 
8393                 tbody.createChild(row);
8394                 
8395                 var _this = this;
8396                 
8397                 if(row.cellObjects.length){
8398                     Roo.each(row.cellObjects, function(r){
8399                         _this.renderCellObject(r);
8400                     })
8401                 }
8402                 
8403             }, this);
8404         }
8405         
8406         var tfoot = this.el.select('tfoot', true).first();
8407         
8408         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8409             
8410             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8411             
8412             var total = this.ds.getTotalCount();
8413             
8414             if(this.footer.pageSize < total){
8415                 this.mainFoot.show();
8416             }
8417         }
8418         
8419         Roo.each(this.el.select('tbody td', true).elements, function(e){
8420             e.on('mouseover', _this.onMouseover, _this);
8421         });
8422         
8423         Roo.each(this.el.select('tbody td', true).elements, function(e){
8424             e.on('mouseout', _this.onMouseout, _this);
8425         });
8426         this.fireEvent('rowsrendered', this);
8427         
8428         this.autoSize();
8429     },
8430     
8431     
8432     onUpdate : function(ds,record)
8433     {
8434         this.refreshRow(record);
8435         this.autoSize();
8436     },
8437     
8438     onRemove : function(ds, record, index, isUpdate){
8439         if(isUpdate !== true){
8440             this.fireEvent("beforerowremoved", this, index, record);
8441         }
8442         var bt = this.mainBody.dom;
8443         
8444         var rows = this.el.select('tbody > tr', true).elements;
8445         
8446         if(typeof(rows[index]) != 'undefined'){
8447             bt.removeChild(rows[index].dom);
8448         }
8449         
8450 //        if(bt.rows[index]){
8451 //            bt.removeChild(bt.rows[index]);
8452 //        }
8453         
8454         if(isUpdate !== true){
8455             //this.stripeRows(index);
8456             //this.syncRowHeights(index, index);
8457             //this.layout();
8458             this.fireEvent("rowremoved", this, index, record);
8459         }
8460     },
8461     
8462     onAdd : function(ds, records, rowIndex)
8463     {
8464         //Roo.log('on Add called');
8465         // - note this does not handle multiple adding very well..
8466         var bt = this.mainBody.dom;
8467         for (var i =0 ; i < records.length;i++) {
8468             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8469             //Roo.log(records[i]);
8470             //Roo.log(this.store.getAt(rowIndex+i));
8471             this.insertRow(this.store, rowIndex + i, false);
8472             return;
8473         }
8474         
8475     },
8476     
8477     
8478     refreshRow : function(record){
8479         var ds = this.store, index;
8480         if(typeof record == 'number'){
8481             index = record;
8482             record = ds.getAt(index);
8483         }else{
8484             index = ds.indexOf(record);
8485             if (index < 0) {
8486                 return; // should not happen - but seems to 
8487             }
8488         }
8489         this.insertRow(ds, index, true);
8490         this.autoSize();
8491         this.onRemove(ds, record, index+1, true);
8492         this.autoSize();
8493         //this.syncRowHeights(index, index);
8494         //this.layout();
8495         this.fireEvent("rowupdated", this, index, record);
8496     },
8497     
8498     insertRow : function(dm, rowIndex, isUpdate){
8499         
8500         if(!isUpdate){
8501             this.fireEvent("beforerowsinserted", this, rowIndex);
8502         }
8503             //var s = this.getScrollState();
8504         var row = this.renderRow(this.cm, this.store, rowIndex);
8505         // insert before rowIndex..
8506         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8507         
8508         var _this = this;
8509                 
8510         if(row.cellObjects.length){
8511             Roo.each(row.cellObjects, function(r){
8512                 _this.renderCellObject(r);
8513             })
8514         }
8515             
8516         if(!isUpdate){
8517             this.fireEvent("rowsinserted", this, rowIndex);
8518             //this.syncRowHeights(firstRow, lastRow);
8519             //this.stripeRows(firstRow);
8520             //this.layout();
8521         }
8522         
8523     },
8524     
8525     
8526     getRowDom : function(rowIndex)
8527     {
8528         var rows = this.el.select('tbody > tr', true).elements;
8529         
8530         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8531         
8532     },
8533     // returns the object tree for a tr..
8534   
8535     
8536     renderRow : function(cm, ds, rowIndex) 
8537     {
8538         var d = ds.getAt(rowIndex);
8539         
8540         var row = {
8541             tag : 'tr',
8542             cls : 'x-row-' + rowIndex,
8543             cn : []
8544         };
8545             
8546         var cellObjects = [];
8547         
8548         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8549             var config = cm.config[i];
8550             
8551             var renderer = cm.getRenderer(i);
8552             var value = '';
8553             var id = false;
8554             
8555             if(typeof(renderer) !== 'undefined'){
8556                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8557             }
8558             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8559             // and are rendered into the cells after the row is rendered - using the id for the element.
8560             
8561             if(typeof(value) === 'object'){
8562                 id = Roo.id();
8563                 cellObjects.push({
8564                     container : id,
8565                     cfg : value 
8566                 })
8567             }
8568             
8569             var rowcfg = {
8570                 record: d,
8571                 rowIndex : rowIndex,
8572                 colIndex : i,
8573                 rowClass : ''
8574             };
8575
8576             this.fireEvent('rowclass', this, rowcfg);
8577             
8578             var td = {
8579                 tag: 'td',
8580                 cls : rowcfg.rowClass + ' x-col-' + i,
8581                 style: '',
8582                 html: (typeof(value) === 'object') ? '' : value
8583             };
8584             
8585             if (id) {
8586                 td.id = id;
8587             }
8588             
8589             if(typeof(config.colspan) != 'undefined'){
8590                 td.colspan = config.colspan;
8591             }
8592             
8593             if(typeof(config.hidden) != 'undefined' && config.hidden){
8594                 td.style += ' display:none;';
8595             }
8596             
8597             if(typeof(config.align) != 'undefined' && config.align.length){
8598                 td.style += ' text-align:' + config.align + ';';
8599             }
8600             if(typeof(config.valign) != 'undefined' && config.valign.length){
8601                 td.style += ' vertical-align:' + config.valign + ';';
8602             }
8603             
8604             if(typeof(config.width) != 'undefined'){
8605                 td.style += ' width:' +  config.width + 'px;';
8606             }
8607             
8608             if(typeof(config.cursor) != 'undefined'){
8609                 td.style += ' cursor:' +  config.cursor + ';';
8610             }
8611             
8612             if(typeof(config.cls) != 'undefined'){
8613                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8614             }
8615             
8616             ['xs','sm','md','lg'].map(function(size){
8617                 
8618                 if(typeof(config[size]) == 'undefined'){
8619                     return;
8620                 }
8621                 
8622                 
8623                   
8624                 if (!config[size]) { // 0 = hidden
8625                     // BS 4 '0' is treated as hide that column and below.
8626                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8627                     return;
8628                 }
8629                 
8630                 td.cls += ' col-' + size + '-' + config[size] + (
8631                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8632                 );
8633                  
8634
8635             });
8636             
8637             row.cn.push(td);
8638            
8639         }
8640         
8641         row.cellObjects = cellObjects;
8642         
8643         return row;
8644           
8645     },
8646     
8647     
8648     
8649     onBeforeLoad : function()
8650     {
8651         
8652     },
8653      /**
8654      * Remove all rows
8655      */
8656     clear : function()
8657     {
8658         this.el.select('tbody', true).first().dom.innerHTML = '';
8659     },
8660     /**
8661      * Show or hide a row.
8662      * @param {Number} rowIndex to show or hide
8663      * @param {Boolean} state hide
8664      */
8665     setRowVisibility : function(rowIndex, state)
8666     {
8667         var bt = this.mainBody.dom;
8668         
8669         var rows = this.el.select('tbody > tr', true).elements;
8670         
8671         if(typeof(rows[rowIndex]) == 'undefined'){
8672             return;
8673         }
8674         rows[rowIndex].dom.style.display = state ? '' : 'none';
8675     },
8676     
8677     
8678     getSelectionModel : function(){
8679         if(!this.selModel){
8680             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8681         }
8682         return this.selModel;
8683     },
8684     /*
8685      * Render the Roo.bootstrap object from renderder
8686      */
8687     renderCellObject : function(r)
8688     {
8689         var _this = this;
8690         
8691         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8692         
8693         var t = r.cfg.render(r.container);
8694         
8695         if(r.cfg.cn){
8696             Roo.each(r.cfg.cn, function(c){
8697                 var child = {
8698                     container: t.getChildContainer(),
8699                     cfg: c
8700                 };
8701                 _this.renderCellObject(child);
8702             })
8703         }
8704     },
8705     
8706     getRowIndex : function(row)
8707     {
8708         var rowIndex = -1;
8709         
8710         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8711             if(el != row){
8712                 return;
8713             }
8714             
8715             rowIndex = index;
8716         });
8717         
8718         return rowIndex;
8719     },
8720      /**
8721      * Returns the grid's underlying element = used by panel.Grid
8722      * @return {Element} The element
8723      */
8724     getGridEl : function(){
8725         return this.el;
8726     },
8727      /**
8728      * Forces a resize - used by panel.Grid
8729      * @return {Element} The element
8730      */
8731     autoSize : function()
8732     {
8733         //var ctr = Roo.get(this.container.dom.parentElement);
8734         var ctr = Roo.get(this.el.dom);
8735         
8736         var thd = this.getGridEl().select('thead',true).first();
8737         var tbd = this.getGridEl().select('tbody', true).first();
8738         var tfd = this.getGridEl().select('tfoot', true).first();
8739         
8740         var cw = ctr.getWidth();
8741         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8742         
8743         if (tbd) {
8744             
8745             tbd.setWidth(ctr.getWidth());
8746             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8747             // this needs fixing for various usage - currently only hydra job advers I think..
8748             //tdb.setHeight(
8749             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8750             //); 
8751             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8752             cw -= barsize;
8753         }
8754         cw = Math.max(cw, this.totalWidth);
8755         this.getGridEl().select('tbody tr',true).setWidth(cw);
8756         
8757         // resize 'expandable coloumn?
8758         
8759         return; // we doe not have a view in this design..
8760         
8761     },
8762     onBodyScroll: function()
8763     {
8764         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8765         if(this.mainHead){
8766             this.mainHead.setStyle({
8767                 'position' : 'relative',
8768                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8769             });
8770         }
8771         
8772         if(this.lazyLoad){
8773             
8774             var scrollHeight = this.mainBody.dom.scrollHeight;
8775             
8776             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8777             
8778             var height = this.mainBody.getHeight();
8779             
8780             if(scrollHeight - height == scrollTop) {
8781                 
8782                 var total = this.ds.getTotalCount();
8783                 
8784                 if(this.footer.cursor + this.footer.pageSize < total){
8785                     
8786                     this.footer.ds.load({
8787                         params : {
8788                             start : this.footer.cursor + this.footer.pageSize,
8789                             limit : this.footer.pageSize
8790                         },
8791                         add : true
8792                     });
8793                 }
8794             }
8795             
8796         }
8797     },
8798     
8799     onHeaderChange : function()
8800     {
8801         var header = this.renderHeader();
8802         var table = this.el.select('table', true).first();
8803         
8804         this.mainHead.remove();
8805         this.mainHead = table.createChild(header, this.mainBody, false);
8806     },
8807     
8808     onHiddenChange : function(colModel, colIndex, hidden)
8809     {
8810         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8811         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8812         
8813         this.CSS.updateRule(thSelector, "display", "");
8814         this.CSS.updateRule(tdSelector, "display", "");
8815         
8816         if(hidden){
8817             this.CSS.updateRule(thSelector, "display", "none");
8818             this.CSS.updateRule(tdSelector, "display", "none");
8819         }
8820         
8821         this.onHeaderChange();
8822         this.onLoad();
8823     },
8824     
8825     setColumnWidth: function(col_index, width)
8826     {
8827         // width = "md-2 xs-2..."
8828         if(!this.colModel.config[col_index]) {
8829             return;
8830         }
8831         
8832         var w = width.split(" ");
8833         
8834         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8835         
8836         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8837         
8838         
8839         for(var j = 0; j < w.length; j++) {
8840             
8841             if(!w[j]) {
8842                 continue;
8843             }
8844             
8845             var size_cls = w[j].split("-");
8846             
8847             if(!Number.isInteger(size_cls[1] * 1)) {
8848                 continue;
8849             }
8850             
8851             if(!this.colModel.config[col_index][size_cls[0]]) {
8852                 continue;
8853             }
8854             
8855             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8856                 continue;
8857             }
8858             
8859             h_row[0].classList.replace(
8860                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8861                 "col-"+size_cls[0]+"-"+size_cls[1]
8862             );
8863             
8864             for(var i = 0; i < rows.length; i++) {
8865                 
8866                 var size_cls = w[j].split("-");
8867                 
8868                 if(!Number.isInteger(size_cls[1] * 1)) {
8869                     continue;
8870                 }
8871                 
8872                 if(!this.colModel.config[col_index][size_cls[0]]) {
8873                     continue;
8874                 }
8875                 
8876                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8877                     continue;
8878                 }
8879                 
8880                 rows[i].classList.replace(
8881                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8882                     "col-"+size_cls[0]+"-"+size_cls[1]
8883                 );
8884             }
8885             
8886             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8887         }
8888     }
8889 });
8890
8891  
8892
8893  /*
8894  * - LGPL
8895  *
8896  * table cell
8897  * 
8898  */
8899
8900 /**
8901  * @class Roo.bootstrap.TableCell
8902  * @extends Roo.bootstrap.Component
8903  * Bootstrap TableCell class
8904  * @cfg {String} html cell contain text
8905  * @cfg {String} cls cell class
8906  * @cfg {String} tag cell tag (td|th) default td
8907  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8908  * @cfg {String} align Aligns the content in a cell
8909  * @cfg {String} axis Categorizes cells
8910  * @cfg {String} bgcolor Specifies the background color of a cell
8911  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8912  * @cfg {Number} colspan Specifies the number of columns a cell should span
8913  * @cfg {String} headers Specifies one or more header cells a cell is related to
8914  * @cfg {Number} height Sets the height of a cell
8915  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8916  * @cfg {Number} rowspan Sets the number of rows a cell should span
8917  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8918  * @cfg {String} valign Vertical aligns the content in a cell
8919  * @cfg {Number} width Specifies the width of a cell
8920  * 
8921  * @constructor
8922  * Create a new TableCell
8923  * @param {Object} config The config object
8924  */
8925
8926 Roo.bootstrap.TableCell = function(config){
8927     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8928 };
8929
8930 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8931     
8932     html: false,
8933     cls: false,
8934     tag: false,
8935     abbr: false,
8936     align: false,
8937     axis: false,
8938     bgcolor: false,
8939     charoff: false,
8940     colspan: false,
8941     headers: false,
8942     height: false,
8943     nowrap: false,
8944     rowspan: false,
8945     scope: false,
8946     valign: false,
8947     width: false,
8948     
8949     
8950     getAutoCreate : function(){
8951         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8952         
8953         cfg = {
8954             tag: 'td'
8955         };
8956         
8957         if(this.tag){
8958             cfg.tag = this.tag;
8959         }
8960         
8961         if (this.html) {
8962             cfg.html=this.html
8963         }
8964         if (this.cls) {
8965             cfg.cls=this.cls
8966         }
8967         if (this.abbr) {
8968             cfg.abbr=this.abbr
8969         }
8970         if (this.align) {
8971             cfg.align=this.align
8972         }
8973         if (this.axis) {
8974             cfg.axis=this.axis
8975         }
8976         if (this.bgcolor) {
8977             cfg.bgcolor=this.bgcolor
8978         }
8979         if (this.charoff) {
8980             cfg.charoff=this.charoff
8981         }
8982         if (this.colspan) {
8983             cfg.colspan=this.colspan
8984         }
8985         if (this.headers) {
8986             cfg.headers=this.headers
8987         }
8988         if (this.height) {
8989             cfg.height=this.height
8990         }
8991         if (this.nowrap) {
8992             cfg.nowrap=this.nowrap
8993         }
8994         if (this.rowspan) {
8995             cfg.rowspan=this.rowspan
8996         }
8997         if (this.scope) {
8998             cfg.scope=this.scope
8999         }
9000         if (this.valign) {
9001             cfg.valign=this.valign
9002         }
9003         if (this.width) {
9004             cfg.width=this.width
9005         }
9006         
9007         
9008         return cfg;
9009     }
9010    
9011 });
9012
9013  
9014
9015  /*
9016  * - LGPL
9017  *
9018  * table row
9019  * 
9020  */
9021
9022 /**
9023  * @class Roo.bootstrap.TableRow
9024  * @extends Roo.bootstrap.Component
9025  * Bootstrap TableRow class
9026  * @cfg {String} cls row class
9027  * @cfg {String} align Aligns the content in a table row
9028  * @cfg {String} bgcolor Specifies a background color for a table row
9029  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9030  * @cfg {String} valign Vertical aligns the content in a table row
9031  * 
9032  * @constructor
9033  * Create a new TableRow
9034  * @param {Object} config The config object
9035  */
9036
9037 Roo.bootstrap.TableRow = function(config){
9038     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9039 };
9040
9041 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9042     
9043     cls: false,
9044     align: false,
9045     bgcolor: false,
9046     charoff: false,
9047     valign: false,
9048     
9049     getAutoCreate : function(){
9050         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9051         
9052         cfg = {
9053             tag: 'tr'
9054         };
9055             
9056         if(this.cls){
9057             cfg.cls = this.cls;
9058         }
9059         if(this.align){
9060             cfg.align = this.align;
9061         }
9062         if(this.bgcolor){
9063             cfg.bgcolor = this.bgcolor;
9064         }
9065         if(this.charoff){
9066             cfg.charoff = this.charoff;
9067         }
9068         if(this.valign){
9069             cfg.valign = this.valign;
9070         }
9071         
9072         return cfg;
9073     }
9074    
9075 });
9076
9077  
9078
9079  /*
9080  * - LGPL
9081  *
9082  * table body
9083  * 
9084  */
9085
9086 /**
9087  * @class Roo.bootstrap.TableBody
9088  * @extends Roo.bootstrap.Component
9089  * Bootstrap TableBody class
9090  * @cfg {String} cls element class
9091  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9092  * @cfg {String} align Aligns the content inside the element
9093  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9094  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9095  * 
9096  * @constructor
9097  * Create a new TableBody
9098  * @param {Object} config The config object
9099  */
9100
9101 Roo.bootstrap.TableBody = function(config){
9102     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9103 };
9104
9105 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9106     
9107     cls: false,
9108     tag: false,
9109     align: false,
9110     charoff: false,
9111     valign: false,
9112     
9113     getAutoCreate : function(){
9114         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9115         
9116         cfg = {
9117             tag: 'tbody'
9118         };
9119             
9120         if (this.cls) {
9121             cfg.cls=this.cls
9122         }
9123         if(this.tag){
9124             cfg.tag = this.tag;
9125         }
9126         
9127         if(this.align){
9128             cfg.align = this.align;
9129         }
9130         if(this.charoff){
9131             cfg.charoff = this.charoff;
9132         }
9133         if(this.valign){
9134             cfg.valign = this.valign;
9135         }
9136         
9137         return cfg;
9138     }
9139     
9140     
9141 //    initEvents : function()
9142 //    {
9143 //        
9144 //        if(!this.store){
9145 //            return;
9146 //        }
9147 //        
9148 //        this.store = Roo.factory(this.store, Roo.data);
9149 //        this.store.on('load', this.onLoad, this);
9150 //        
9151 //        this.store.load();
9152 //        
9153 //    },
9154 //    
9155 //    onLoad: function () 
9156 //    {   
9157 //        this.fireEvent('load', this);
9158 //    }
9159 //    
9160 //   
9161 });
9162
9163  
9164
9165  /*
9166  * Based on:
9167  * Ext JS Library 1.1.1
9168  * Copyright(c) 2006-2007, Ext JS, LLC.
9169  *
9170  * Originally Released Under LGPL - original licence link has changed is not relivant.
9171  *
9172  * Fork - LGPL
9173  * <script type="text/javascript">
9174  */
9175
9176 // as we use this in bootstrap.
9177 Roo.namespace('Roo.form');
9178  /**
9179  * @class Roo.form.Action
9180  * Internal Class used to handle form actions
9181  * @constructor
9182  * @param {Roo.form.BasicForm} el The form element or its id
9183  * @param {Object} config Configuration options
9184  */
9185
9186  
9187  
9188 // define the action interface
9189 Roo.form.Action = function(form, options){
9190     this.form = form;
9191     this.options = options || {};
9192 };
9193 /**
9194  * Client Validation Failed
9195  * @const 
9196  */
9197 Roo.form.Action.CLIENT_INVALID = 'client';
9198 /**
9199  * Server Validation Failed
9200  * @const 
9201  */
9202 Roo.form.Action.SERVER_INVALID = 'server';
9203  /**
9204  * Connect to Server Failed
9205  * @const 
9206  */
9207 Roo.form.Action.CONNECT_FAILURE = 'connect';
9208 /**
9209  * Reading Data from Server Failed
9210  * @const 
9211  */
9212 Roo.form.Action.LOAD_FAILURE = 'load';
9213
9214 Roo.form.Action.prototype = {
9215     type : 'default',
9216     failureType : undefined,
9217     response : undefined,
9218     result : undefined,
9219
9220     // interface method
9221     run : function(options){
9222
9223     },
9224
9225     // interface method
9226     success : function(response){
9227
9228     },
9229
9230     // interface method
9231     handleResponse : function(response){
9232
9233     },
9234
9235     // default connection failure
9236     failure : function(response){
9237         
9238         this.response = response;
9239         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9240         this.form.afterAction(this, false);
9241     },
9242
9243     processResponse : function(response){
9244         this.response = response;
9245         if(!response.responseText){
9246             return true;
9247         }
9248         this.result = this.handleResponse(response);
9249         return this.result;
9250     },
9251
9252     // utility functions used internally
9253     getUrl : function(appendParams){
9254         var url = this.options.url || this.form.url || this.form.el.dom.action;
9255         if(appendParams){
9256             var p = this.getParams();
9257             if(p){
9258                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9259             }
9260         }
9261         return url;
9262     },
9263
9264     getMethod : function(){
9265         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9266     },
9267
9268     getParams : function(){
9269         var bp = this.form.baseParams;
9270         var p = this.options.params;
9271         if(p){
9272             if(typeof p == "object"){
9273                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9274             }else if(typeof p == 'string' && bp){
9275                 p += '&' + Roo.urlEncode(bp);
9276             }
9277         }else if(bp){
9278             p = Roo.urlEncode(bp);
9279         }
9280         return p;
9281     },
9282
9283     createCallback : function(){
9284         return {
9285             success: this.success,
9286             failure: this.failure,
9287             scope: this,
9288             timeout: (this.form.timeout*1000),
9289             upload: this.form.fileUpload ? this.success : undefined
9290         };
9291     }
9292 };
9293
9294 Roo.form.Action.Submit = function(form, options){
9295     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9296 };
9297
9298 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9299     type : 'submit',
9300
9301     haveProgress : false,
9302     uploadComplete : false,
9303     
9304     // uploadProgress indicator.
9305     uploadProgress : function()
9306     {
9307         if (!this.form.progressUrl) {
9308             return;
9309         }
9310         
9311         if (!this.haveProgress) {
9312             Roo.MessageBox.progress("Uploading", "Uploading");
9313         }
9314         if (this.uploadComplete) {
9315            Roo.MessageBox.hide();
9316            return;
9317         }
9318         
9319         this.haveProgress = true;
9320    
9321         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9322         
9323         var c = new Roo.data.Connection();
9324         c.request({
9325             url : this.form.progressUrl,
9326             params: {
9327                 id : uid
9328             },
9329             method: 'GET',
9330             success : function(req){
9331                //console.log(data);
9332                 var rdata = false;
9333                 var edata;
9334                 try  {
9335                    rdata = Roo.decode(req.responseText)
9336                 } catch (e) {
9337                     Roo.log("Invalid data from server..");
9338                     Roo.log(edata);
9339                     return;
9340                 }
9341                 if (!rdata || !rdata.success) {
9342                     Roo.log(rdata);
9343                     Roo.MessageBox.alert(Roo.encode(rdata));
9344                     return;
9345                 }
9346                 var data = rdata.data;
9347                 
9348                 if (this.uploadComplete) {
9349                    Roo.MessageBox.hide();
9350                    return;
9351                 }
9352                    
9353                 if (data){
9354                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9355                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9356                     );
9357                 }
9358                 this.uploadProgress.defer(2000,this);
9359             },
9360        
9361             failure: function(data) {
9362                 Roo.log('progress url failed ');
9363                 Roo.log(data);
9364             },
9365             scope : this
9366         });
9367            
9368     },
9369     
9370     
9371     run : function()
9372     {
9373         // run get Values on the form, so it syncs any secondary forms.
9374         this.form.getValues();
9375         
9376         var o = this.options;
9377         var method = this.getMethod();
9378         var isPost = method == 'POST';
9379         if(o.clientValidation === false || this.form.isValid()){
9380             
9381             if (this.form.progressUrl) {
9382                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9383                     (new Date() * 1) + '' + Math.random());
9384                     
9385             } 
9386             
9387             
9388             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9389                 form:this.form.el.dom,
9390                 url:this.getUrl(!isPost),
9391                 method: method,
9392                 params:isPost ? this.getParams() : null,
9393                 isUpload: this.form.fileUpload,
9394                 formData : this.form.formData
9395             }));
9396             
9397             this.uploadProgress();
9398
9399         }else if (o.clientValidation !== false){ // client validation failed
9400             this.failureType = Roo.form.Action.CLIENT_INVALID;
9401             this.form.afterAction(this, false);
9402         }
9403     },
9404
9405     success : function(response)
9406     {
9407         this.uploadComplete= true;
9408         if (this.haveProgress) {
9409             Roo.MessageBox.hide();
9410         }
9411         
9412         
9413         var result = this.processResponse(response);
9414         if(result === true || result.success){
9415             this.form.afterAction(this, true);
9416             return;
9417         }
9418         if(result.errors){
9419             this.form.markInvalid(result.errors);
9420             this.failureType = Roo.form.Action.SERVER_INVALID;
9421         }
9422         this.form.afterAction(this, false);
9423     },
9424     failure : function(response)
9425     {
9426         this.uploadComplete= true;
9427         if (this.haveProgress) {
9428             Roo.MessageBox.hide();
9429         }
9430         
9431         this.response = response;
9432         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9433         this.form.afterAction(this, false);
9434     },
9435     
9436     handleResponse : function(response){
9437         if(this.form.errorReader){
9438             var rs = this.form.errorReader.read(response);
9439             var errors = [];
9440             if(rs.records){
9441                 for(var i = 0, len = rs.records.length; i < len; i++) {
9442                     var r = rs.records[i];
9443                     errors[i] = r.data;
9444                 }
9445             }
9446             if(errors.length < 1){
9447                 errors = null;
9448             }
9449             return {
9450                 success : rs.success,
9451                 errors : errors
9452             };
9453         }
9454         var ret = false;
9455         try {
9456             ret = Roo.decode(response.responseText);
9457         } catch (e) {
9458             ret = {
9459                 success: false,
9460                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9461                 errors : []
9462             };
9463         }
9464         return ret;
9465         
9466     }
9467 });
9468
9469
9470 Roo.form.Action.Load = function(form, options){
9471     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9472     this.reader = this.form.reader;
9473 };
9474
9475 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9476     type : 'load',
9477
9478     run : function(){
9479         
9480         Roo.Ajax.request(Roo.apply(
9481                 this.createCallback(), {
9482                     method:this.getMethod(),
9483                     url:this.getUrl(false),
9484                     params:this.getParams()
9485         }));
9486     },
9487
9488     success : function(response){
9489         
9490         var result = this.processResponse(response);
9491         if(result === true || !result.success || !result.data){
9492             this.failureType = Roo.form.Action.LOAD_FAILURE;
9493             this.form.afterAction(this, false);
9494             return;
9495         }
9496         this.form.clearInvalid();
9497         this.form.setValues(result.data);
9498         this.form.afterAction(this, true);
9499     },
9500
9501     handleResponse : function(response){
9502         if(this.form.reader){
9503             var rs = this.form.reader.read(response);
9504             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9505             return {
9506                 success : rs.success,
9507                 data : data
9508             };
9509         }
9510         return Roo.decode(response.responseText);
9511     }
9512 });
9513
9514 Roo.form.Action.ACTION_TYPES = {
9515     'load' : Roo.form.Action.Load,
9516     'submit' : Roo.form.Action.Submit
9517 };/*
9518  * - LGPL
9519  *
9520  * form
9521  *
9522  */
9523
9524 /**
9525  * @class Roo.bootstrap.Form
9526  * @extends Roo.bootstrap.Component
9527  * Bootstrap Form class
9528  * @cfg {String} method  GET | POST (default POST)
9529  * @cfg {String} labelAlign top | left (default top)
9530  * @cfg {String} align left  | right - for navbars
9531  * @cfg {Boolean} loadMask load mask when submit (default true)
9532
9533  *
9534  * @constructor
9535  * Create a new Form
9536  * @param {Object} config The config object
9537  */
9538
9539
9540 Roo.bootstrap.Form = function(config){
9541     
9542     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9543     
9544     Roo.bootstrap.Form.popover.apply();
9545     
9546     this.addEvents({
9547         /**
9548          * @event clientvalidation
9549          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9550          * @param {Form} this
9551          * @param {Boolean} valid true if the form has passed client-side validation
9552          */
9553         clientvalidation: true,
9554         /**
9555          * @event beforeaction
9556          * Fires before any action is performed. Return false to cancel the action.
9557          * @param {Form} this
9558          * @param {Action} action The action to be performed
9559          */
9560         beforeaction: true,
9561         /**
9562          * @event actionfailed
9563          * Fires when an action fails.
9564          * @param {Form} this
9565          * @param {Action} action The action that failed
9566          */
9567         actionfailed : true,
9568         /**
9569          * @event actioncomplete
9570          * Fires when an action is completed.
9571          * @param {Form} this
9572          * @param {Action} action The action that completed
9573          */
9574         actioncomplete : true
9575     });
9576 };
9577
9578 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9579
9580      /**
9581      * @cfg {String} method
9582      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9583      */
9584     method : 'POST',
9585     /**
9586      * @cfg {String} url
9587      * The URL to use for form actions if one isn't supplied in the action options.
9588      */
9589     /**
9590      * @cfg {Boolean} fileUpload
9591      * Set to true if this form is a file upload.
9592      */
9593
9594     /**
9595      * @cfg {Object} baseParams
9596      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9597      */
9598
9599     /**
9600      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9601      */
9602     timeout: 30,
9603     /**
9604      * @cfg {Sting} align (left|right) for navbar forms
9605      */
9606     align : 'left',
9607
9608     // private
9609     activeAction : null,
9610
9611     /**
9612      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9613      * element by passing it or its id or mask the form itself by passing in true.
9614      * @type Mixed
9615      */
9616     waitMsgTarget : false,
9617
9618     loadMask : true,
9619     
9620     /**
9621      * @cfg {Boolean} errorMask (true|false) default false
9622      */
9623     errorMask : false,
9624     
9625     /**
9626      * @cfg {Number} maskOffset Default 100
9627      */
9628     maskOffset : 100,
9629     
9630     /**
9631      * @cfg {Boolean} maskBody
9632      */
9633     maskBody : false,
9634
9635     getAutoCreate : function(){
9636
9637         var cfg = {
9638             tag: 'form',
9639             method : this.method || 'POST',
9640             id : this.id || Roo.id(),
9641             cls : ''
9642         };
9643         if (this.parent().xtype.match(/^Nav/)) {
9644             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9645
9646         }
9647
9648         if (this.labelAlign == 'left' ) {
9649             cfg.cls += ' form-horizontal';
9650         }
9651
9652
9653         return cfg;
9654     },
9655     initEvents : function()
9656     {
9657         this.el.on('submit', this.onSubmit, this);
9658         // this was added as random key presses on the form where triggering form submit.
9659         this.el.on('keypress', function(e) {
9660             if (e.getCharCode() != 13) {
9661                 return true;
9662             }
9663             // we might need to allow it for textareas.. and some other items.
9664             // check e.getTarget().
9665
9666             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9667                 return true;
9668             }
9669
9670             Roo.log("keypress blocked");
9671
9672             e.preventDefault();
9673             return false;
9674         });
9675         
9676     },
9677     // private
9678     onSubmit : function(e){
9679         e.stopEvent();
9680     },
9681
9682      /**
9683      * Returns true if client-side validation on the form is successful.
9684      * @return Boolean
9685      */
9686     isValid : function(){
9687         var items = this.getItems();
9688         var valid = true;
9689         var target = false;
9690         
9691         items.each(function(f){
9692             
9693             if(f.validate()){
9694                 return;
9695             }
9696             
9697             Roo.log('invalid field: ' + f.name);
9698             
9699             valid = false;
9700
9701             if(!target && f.el.isVisible(true)){
9702                 target = f;
9703             }
9704            
9705         });
9706         
9707         if(this.errorMask && !valid){
9708             Roo.bootstrap.Form.popover.mask(this, target);
9709         }
9710         
9711         return valid;
9712     },
9713     
9714     /**
9715      * Returns true if any fields in this form have changed since their original load.
9716      * @return Boolean
9717      */
9718     isDirty : function(){
9719         var dirty = false;
9720         var items = this.getItems();
9721         items.each(function(f){
9722            if(f.isDirty()){
9723                dirty = true;
9724                return false;
9725            }
9726            return true;
9727         });
9728         return dirty;
9729     },
9730      /**
9731      * Performs a predefined action (submit or load) or custom actions you define on this form.
9732      * @param {String} actionName The name of the action type
9733      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9734      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9735      * accept other config options):
9736      * <pre>
9737 Property          Type             Description
9738 ----------------  ---------------  ----------------------------------------------------------------------------------
9739 url               String           The url for the action (defaults to the form's url)
9740 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9741 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9742 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9743                                    validate the form on the client (defaults to false)
9744      * </pre>
9745      * @return {BasicForm} this
9746      */
9747     doAction : function(action, options){
9748         if(typeof action == 'string'){
9749             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9750         }
9751         if(this.fireEvent('beforeaction', this, action) !== false){
9752             this.beforeAction(action);
9753             action.run.defer(100, action);
9754         }
9755         return this;
9756     },
9757
9758     // private
9759     beforeAction : function(action){
9760         var o = action.options;
9761         
9762         if(this.loadMask){
9763             
9764             if(this.maskBody){
9765                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9766             } else {
9767                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9768             }
9769         }
9770         // not really supported yet.. ??
9771
9772         //if(this.waitMsgTarget === true){
9773         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else if(this.waitMsgTarget){
9775         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9776         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9777         //}else {
9778         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9779        // }
9780
9781     },
9782
9783     // private
9784     afterAction : function(action, success){
9785         this.activeAction = null;
9786         var o = action.options;
9787
9788         if(this.loadMask){
9789             
9790             if(this.maskBody){
9791                 Roo.get(document.body).unmask();
9792             } else {
9793                 this.el.unmask();
9794             }
9795         }
9796         
9797         //if(this.waitMsgTarget === true){
9798 //            this.el.unmask();
9799         //}else if(this.waitMsgTarget){
9800         //    this.waitMsgTarget.unmask();
9801         //}else{
9802         //    Roo.MessageBox.updateProgress(1);
9803         //    Roo.MessageBox.hide();
9804        // }
9805         //
9806         if(success){
9807             if(o.reset){
9808                 this.reset();
9809             }
9810             Roo.callback(o.success, o.scope, [this, action]);
9811             this.fireEvent('actioncomplete', this, action);
9812
9813         }else{
9814
9815             // failure condition..
9816             // we have a scenario where updates need confirming.
9817             // eg. if a locking scenario exists..
9818             // we look for { errors : { needs_confirm : true }} in the response.
9819             if (
9820                 (typeof(action.result) != 'undefined')  &&
9821                 (typeof(action.result.errors) != 'undefined')  &&
9822                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9823            ){
9824                 var _t = this;
9825                 Roo.log("not supported yet");
9826                  /*
9827
9828                 Roo.MessageBox.confirm(
9829                     "Change requires confirmation",
9830                     action.result.errorMsg,
9831                     function(r) {
9832                         if (r != 'yes') {
9833                             return;
9834                         }
9835                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9836                     }
9837
9838                 );
9839                 */
9840
9841
9842                 return;
9843             }
9844
9845             Roo.callback(o.failure, o.scope, [this, action]);
9846             // show an error message if no failed handler is set..
9847             if (!this.hasListener('actionfailed')) {
9848                 Roo.log("need to add dialog support");
9849                 /*
9850                 Roo.MessageBox.alert("Error",
9851                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9852                         action.result.errorMsg :
9853                         "Saving Failed, please check your entries or try again"
9854                 );
9855                 */
9856             }
9857
9858             this.fireEvent('actionfailed', this, action);
9859         }
9860
9861     },
9862     /**
9863      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9864      * @param {String} id The value to search for
9865      * @return Field
9866      */
9867     findField : function(id){
9868         var items = this.getItems();
9869         var field = items.get(id);
9870         if(!field){
9871              items.each(function(f){
9872                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9873                     field = f;
9874                     return false;
9875                 }
9876                 return true;
9877             });
9878         }
9879         return field || null;
9880     },
9881      /**
9882      * Mark fields in this form invalid in bulk.
9883      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9884      * @return {BasicForm} this
9885      */
9886     markInvalid : function(errors){
9887         if(errors instanceof Array){
9888             for(var i = 0, len = errors.length; i < len; i++){
9889                 var fieldError = errors[i];
9890                 var f = this.findField(fieldError.id);
9891                 if(f){
9892                     f.markInvalid(fieldError.msg);
9893                 }
9894             }
9895         }else{
9896             var field, id;
9897             for(id in errors){
9898                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9899                     field.markInvalid(errors[id]);
9900                 }
9901             }
9902         }
9903         //Roo.each(this.childForms || [], function (f) {
9904         //    f.markInvalid(errors);
9905         //});
9906
9907         return this;
9908     },
9909
9910     /**
9911      * Set values for fields in this form in bulk.
9912      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9913      * @return {BasicForm} this
9914      */
9915     setValues : function(values){
9916         if(values instanceof Array){ // array of objects
9917             for(var i = 0, len = values.length; i < len; i++){
9918                 var v = values[i];
9919                 var f = this.findField(v.id);
9920                 if(f){
9921                     f.setValue(v.value);
9922                     if(this.trackResetOnLoad){
9923                         f.originalValue = f.getValue();
9924                     }
9925                 }
9926             }
9927         }else{ // object hash
9928             var field, id;
9929             for(id in values){
9930                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9931
9932                     if (field.setFromData &&
9933                         field.valueField &&
9934                         field.displayField &&
9935                         // combos' with local stores can
9936                         // be queried via setValue()
9937                         // to set their value..
9938                         (field.store && !field.store.isLocal)
9939                         ) {
9940                         // it's a combo
9941                         var sd = { };
9942                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9943                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9944                         field.setFromData(sd);
9945
9946                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9947                         
9948                         field.setFromData(values);
9949                         
9950                     } else {
9951                         field.setValue(values[id]);
9952                     }
9953
9954
9955                     if(this.trackResetOnLoad){
9956                         field.originalValue = field.getValue();
9957                     }
9958                 }
9959             }
9960         }
9961
9962         //Roo.each(this.childForms || [], function (f) {
9963         //    f.setValues(values);
9964         //});
9965
9966         return this;
9967     },
9968
9969     /**
9970      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9971      * they are returned as an array.
9972      * @param {Boolean} asString
9973      * @return {Object}
9974      */
9975     getValues : function(asString){
9976         //if (this.childForms) {
9977             // copy values from the child forms
9978         //    Roo.each(this.childForms, function (f) {
9979         //        this.setValues(f.getValues());
9980         //    }, this);
9981         //}
9982
9983
9984
9985         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9986         if(asString === true){
9987             return fs;
9988         }
9989         return Roo.urlDecode(fs);
9990     },
9991
9992     /**
9993      * Returns the fields in this form as an object with key/value pairs.
9994      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9995      * @return {Object}
9996      */
9997     getFieldValues : function(with_hidden)
9998     {
9999         var items = this.getItems();
10000         var ret = {};
10001         items.each(function(f){
10002             
10003             if (!f.getName()) {
10004                 return;
10005             }
10006             
10007             var v = f.getValue();
10008             
10009             if (f.inputType =='radio') {
10010                 if (typeof(ret[f.getName()]) == 'undefined') {
10011                     ret[f.getName()] = ''; // empty..
10012                 }
10013
10014                 if (!f.el.dom.checked) {
10015                     return;
10016
10017                 }
10018                 v = f.el.dom.value;
10019
10020             }
10021             
10022             if(f.xtype == 'MoneyField'){
10023                 ret[f.currencyName] = f.getCurrency();
10024             }
10025
10026             // not sure if this supported any more..
10027             if ((typeof(v) == 'object') && f.getRawValue) {
10028                 v = f.getRawValue() ; // dates..
10029             }
10030             // combo boxes where name != hiddenName...
10031             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10032                 ret[f.name] = f.getRawValue();
10033             }
10034             ret[f.getName()] = v;
10035         });
10036
10037         return ret;
10038     },
10039
10040     /**
10041      * Clears all invalid messages in this form.
10042      * @return {BasicForm} this
10043      */
10044     clearInvalid : function(){
10045         var items = this.getItems();
10046
10047         items.each(function(f){
10048            f.clearInvalid();
10049         });
10050
10051         return this;
10052     },
10053
10054     /**
10055      * Resets this form.
10056      * @return {BasicForm} this
10057      */
10058     reset : function(){
10059         var items = this.getItems();
10060         items.each(function(f){
10061             f.reset();
10062         });
10063
10064         Roo.each(this.childForms || [], function (f) {
10065             f.reset();
10066         });
10067
10068
10069         return this;
10070     },
10071     
10072     getItems : function()
10073     {
10074         var r=new Roo.util.MixedCollection(false, function(o){
10075             return o.id || (o.id = Roo.id());
10076         });
10077         var iter = function(el) {
10078             if (el.inputEl) {
10079                 r.add(el);
10080             }
10081             if (!el.items) {
10082                 return;
10083             }
10084             Roo.each(el.items,function(e) {
10085                 iter(e);
10086             });
10087         };
10088
10089         iter(this);
10090         return r;
10091     },
10092     
10093     hideFields : function(items)
10094     {
10095         Roo.each(items, function(i){
10096             
10097             var f = this.findField(i);
10098             
10099             if(!f){
10100                 return;
10101             }
10102             
10103             f.hide();
10104             
10105         }, this);
10106     },
10107     
10108     showFields : function(items)
10109     {
10110         Roo.each(items, function(i){
10111             
10112             var f = this.findField(i);
10113             
10114             if(!f){
10115                 return;
10116             }
10117             
10118             f.show();
10119             
10120         }, this);
10121     }
10122
10123 });
10124
10125 Roo.apply(Roo.bootstrap.Form, {
10126     
10127     popover : {
10128         
10129         padding : 5,
10130         
10131         isApplied : false,
10132         
10133         isMasked : false,
10134         
10135         form : false,
10136         
10137         target : false,
10138         
10139         toolTip : false,
10140         
10141         intervalID : false,
10142         
10143         maskEl : false,
10144         
10145         apply : function()
10146         {
10147             if(this.isApplied){
10148                 return;
10149             }
10150             
10151             this.maskEl = {
10152                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10153                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10154                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10155                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10156             };
10157             
10158             this.maskEl.top.enableDisplayMode("block");
10159             this.maskEl.left.enableDisplayMode("block");
10160             this.maskEl.bottom.enableDisplayMode("block");
10161             this.maskEl.right.enableDisplayMode("block");
10162             
10163             this.toolTip = new Roo.bootstrap.Tooltip({
10164                 cls : 'roo-form-error-popover',
10165                 alignment : {
10166                     'left' : ['r-l', [-2,0], 'right'],
10167                     'right' : ['l-r', [2,0], 'left'],
10168                     'bottom' : ['tl-bl', [0,2], 'top'],
10169                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10170                 }
10171             });
10172             
10173             this.toolTip.render(Roo.get(document.body));
10174
10175             this.toolTip.el.enableDisplayMode("block");
10176             
10177             Roo.get(document.body).on('click', function(){
10178                 this.unmask();
10179             }, this);
10180             
10181             Roo.get(document.body).on('touchstart', function(){
10182                 this.unmask();
10183             }, this);
10184             
10185             this.isApplied = true
10186         },
10187         
10188         mask : function(form, target)
10189         {
10190             this.form = form;
10191             
10192             this.target = target;
10193             
10194             if(!this.form.errorMask || !target.el){
10195                 return;
10196             }
10197             
10198             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10199             
10200             Roo.log(scrollable);
10201             
10202             var ot = this.target.el.calcOffsetsTo(scrollable);
10203             
10204             var scrollTo = ot[1] - this.form.maskOffset;
10205             
10206             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10207             
10208             scrollable.scrollTo('top', scrollTo);
10209             
10210             var box = this.target.el.getBox();
10211             Roo.log(box);
10212             var zIndex = Roo.bootstrap.Modal.zIndex++;
10213
10214             
10215             this.maskEl.top.setStyle('position', 'absolute');
10216             this.maskEl.top.setStyle('z-index', zIndex);
10217             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10218             this.maskEl.top.setLeft(0);
10219             this.maskEl.top.setTop(0);
10220             this.maskEl.top.show();
10221             
10222             this.maskEl.left.setStyle('position', 'absolute');
10223             this.maskEl.left.setStyle('z-index', zIndex);
10224             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10225             this.maskEl.left.setLeft(0);
10226             this.maskEl.left.setTop(box.y - this.padding);
10227             this.maskEl.left.show();
10228
10229             this.maskEl.bottom.setStyle('position', 'absolute');
10230             this.maskEl.bottom.setStyle('z-index', zIndex);
10231             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10232             this.maskEl.bottom.setLeft(0);
10233             this.maskEl.bottom.setTop(box.bottom + this.padding);
10234             this.maskEl.bottom.show();
10235
10236             this.maskEl.right.setStyle('position', 'absolute');
10237             this.maskEl.right.setStyle('z-index', zIndex);
10238             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10239             this.maskEl.right.setLeft(box.right + this.padding);
10240             this.maskEl.right.setTop(box.y - this.padding);
10241             this.maskEl.right.show();
10242
10243             this.toolTip.bindEl = this.target.el;
10244
10245             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10246
10247             var tip = this.target.blankText;
10248
10249             if(this.target.getValue() !== '' ) {
10250                 
10251                 if (this.target.invalidText.length) {
10252                     tip = this.target.invalidText;
10253                 } else if (this.target.regexText.length){
10254                     tip = this.target.regexText;
10255                 }
10256             }
10257
10258             this.toolTip.show(tip);
10259
10260             this.intervalID = window.setInterval(function() {
10261                 Roo.bootstrap.Form.popover.unmask();
10262             }, 10000);
10263
10264             window.onwheel = function(){ return false;};
10265             
10266             (function(){ this.isMasked = true; }).defer(500, this);
10267             
10268         },
10269         
10270         unmask : function()
10271         {
10272             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10273                 return;
10274             }
10275             
10276             this.maskEl.top.setStyle('position', 'absolute');
10277             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.top.hide();
10279
10280             this.maskEl.left.setStyle('position', 'absolute');
10281             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.left.hide();
10283
10284             this.maskEl.bottom.setStyle('position', 'absolute');
10285             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10286             this.maskEl.bottom.hide();
10287
10288             this.maskEl.right.setStyle('position', 'absolute');
10289             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10290             this.maskEl.right.hide();
10291             
10292             this.toolTip.hide();
10293             
10294             this.toolTip.el.hide();
10295             
10296             window.onwheel = function(){ return true;};
10297             
10298             if(this.intervalID){
10299                 window.clearInterval(this.intervalID);
10300                 this.intervalID = false;
10301             }
10302             
10303             this.isMasked = false;
10304             
10305         }
10306         
10307     }
10308     
10309 });
10310
10311 /*
10312  * Based on:
10313  * Ext JS Library 1.1.1
10314  * Copyright(c) 2006-2007, Ext JS, LLC.
10315  *
10316  * Originally Released Under LGPL - original licence link has changed is not relivant.
10317  *
10318  * Fork - LGPL
10319  * <script type="text/javascript">
10320  */
10321 /**
10322  * @class Roo.form.VTypes
10323  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10324  * @singleton
10325  */
10326 Roo.form.VTypes = function(){
10327     // closure these in so they are only created once.
10328     var alpha = /^[a-zA-Z_]+$/;
10329     var alphanum = /^[a-zA-Z0-9_]+$/;
10330     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10331     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10332
10333     // All these messages and functions are configurable
10334     return {
10335         /**
10336          * The function used to validate email addresses
10337          * @param {String} value The email address
10338          */
10339         'email' : function(v){
10340             return email.test(v);
10341         },
10342         /**
10343          * The error text to display when the email validation function returns false
10344          * @type String
10345          */
10346         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10347         /**
10348          * The keystroke filter mask to be applied on email input
10349          * @type RegExp
10350          */
10351         'emailMask' : /[a-z0-9_\.\-@]/i,
10352
10353         /**
10354          * The function used to validate URLs
10355          * @param {String} value The URL
10356          */
10357         'url' : function(v){
10358             return url.test(v);
10359         },
10360         /**
10361          * The error text to display when the url validation function returns false
10362          * @type String
10363          */
10364         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10365         
10366         /**
10367          * The function used to validate alpha values
10368          * @param {String} value The value
10369          */
10370         'alpha' : function(v){
10371             return alpha.test(v);
10372         },
10373         /**
10374          * The error text to display when the alpha validation function returns false
10375          * @type String
10376          */
10377         'alphaText' : 'This field should only contain letters and _',
10378         /**
10379          * The keystroke filter mask to be applied on alpha input
10380          * @type RegExp
10381          */
10382         'alphaMask' : /[a-z_]/i,
10383
10384         /**
10385          * The function used to validate alphanumeric values
10386          * @param {String} value The value
10387          */
10388         'alphanum' : function(v){
10389             return alphanum.test(v);
10390         },
10391         /**
10392          * The error text to display when the alphanumeric validation function returns false
10393          * @type String
10394          */
10395         'alphanumText' : 'This field should only contain letters, numbers and _',
10396         /**
10397          * The keystroke filter mask to be applied on alphanumeric input
10398          * @type RegExp
10399          */
10400         'alphanumMask' : /[a-z0-9_]/i
10401     };
10402 }();/*
10403  * - LGPL
10404  *
10405  * Input
10406  * 
10407  */
10408
10409 /**
10410  * @class Roo.bootstrap.Input
10411  * @extends Roo.bootstrap.Component
10412  * Bootstrap Input class
10413  * @cfg {Boolean} disabled is it disabled
10414  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10415  * @cfg {String} name name of the input
10416  * @cfg {string} fieldLabel - the label associated
10417  * @cfg {string} placeholder - placeholder to put in text.
10418  * @cfg {string}  before - input group add on before
10419  * @cfg {string} after - input group add on after
10420  * @cfg {string} size - (lg|sm) or leave empty..
10421  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10422  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10423  * @cfg {Number} md colspan out of 12 for computer-sized screens
10424  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10425  * @cfg {string} value default value of the input
10426  * @cfg {Number} labelWidth set the width of label 
10427  * @cfg {Number} labellg set the width of label (1-12)
10428  * @cfg {Number} labelmd set the width of label (1-12)
10429  * @cfg {Number} labelsm set the width of label (1-12)
10430  * @cfg {Number} labelxs set the width of label (1-12)
10431  * @cfg {String} labelAlign (top|left)
10432  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10433  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10434  * @cfg {String} indicatorpos (left|right) default left
10435  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10436  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10437  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10438
10439  * @cfg {String} align (left|center|right) Default left
10440  * @cfg {Boolean} forceFeedback (true|false) Default false
10441  * 
10442  * @constructor
10443  * Create a new Input
10444  * @param {Object} config The config object
10445  */
10446
10447 Roo.bootstrap.Input = function(config){
10448     
10449     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10450     
10451     this.addEvents({
10452         /**
10453          * @event focus
10454          * Fires when this field receives input focus.
10455          * @param {Roo.form.Field} this
10456          */
10457         focus : true,
10458         /**
10459          * @event blur
10460          * Fires when this field loses input focus.
10461          * @param {Roo.form.Field} this
10462          */
10463         blur : true,
10464         /**
10465          * @event specialkey
10466          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10467          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10468          * @param {Roo.form.Field} this
10469          * @param {Roo.EventObject} e The event object
10470          */
10471         specialkey : true,
10472         /**
10473          * @event change
10474          * Fires just before the field blurs if the field value has changed.
10475          * @param {Roo.form.Field} this
10476          * @param {Mixed} newValue The new value
10477          * @param {Mixed} oldValue The original value
10478          */
10479         change : true,
10480         /**
10481          * @event invalid
10482          * Fires after the field has been marked as invalid.
10483          * @param {Roo.form.Field} this
10484          * @param {String} msg The validation message
10485          */
10486         invalid : true,
10487         /**
10488          * @event valid
10489          * Fires after the field has been validated with no errors.
10490          * @param {Roo.form.Field} this
10491          */
10492         valid : true,
10493          /**
10494          * @event keyup
10495          * Fires after the key up
10496          * @param {Roo.form.Field} this
10497          * @param {Roo.EventObject}  e The event Object
10498          */
10499         keyup : true,
10500         /**
10501          * @event paste
10502          * Fires after the user pastes into input
10503          * @param {Roo.form.Field} this
10504          * @param {Roo.EventObject}  e The event Object
10505          */
10506         paste : true
10507     });
10508 };
10509
10510 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10511      /**
10512      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10513       automatic validation (defaults to "keyup").
10514      */
10515     validationEvent : "keyup",
10516      /**
10517      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10518      */
10519     validateOnBlur : true,
10520     /**
10521      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10522      */
10523     validationDelay : 250,
10524      /**
10525      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10526      */
10527     focusClass : "x-form-focus",  // not needed???
10528     
10529        
10530     /**
10531      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10532      */
10533     invalidClass : "has-warning",
10534     
10535     /**
10536      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10537      */
10538     validClass : "has-success",
10539     
10540     /**
10541      * @cfg {Boolean} hasFeedback (true|false) default true
10542      */
10543     hasFeedback : true,
10544     
10545     /**
10546      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10547      */
10548     invalidFeedbackClass : "glyphicon-warning-sign",
10549     
10550     /**
10551      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10552      */
10553     validFeedbackClass : "glyphicon-ok",
10554     
10555     /**
10556      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10557      */
10558     selectOnFocus : false,
10559     
10560      /**
10561      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10562      */
10563     maskRe : null,
10564        /**
10565      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10566      */
10567     vtype : null,
10568     
10569       /**
10570      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10571      */
10572     disableKeyFilter : false,
10573     
10574        /**
10575      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10576      */
10577     disabled : false,
10578      /**
10579      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10580      */
10581     allowBlank : true,
10582     /**
10583      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10584      */
10585     blankText : "Please complete this mandatory field",
10586     
10587      /**
10588      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10589      */
10590     minLength : 0,
10591     /**
10592      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10593      */
10594     maxLength : Number.MAX_VALUE,
10595     /**
10596      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10597      */
10598     minLengthText : "The minimum length for this field is {0}",
10599     /**
10600      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10601      */
10602     maxLengthText : "The maximum length for this field is {0}",
10603   
10604     
10605     /**
10606      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10607      * If available, this function will be called only after the basic validators all return true, and will be passed the
10608      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10609      */
10610     validator : null,
10611     /**
10612      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10613      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10614      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10615      */
10616     regex : null,
10617     /**
10618      * @cfg {String} regexText -- Depricated - use Invalid Text
10619      */
10620     regexText : "",
10621     
10622     /**
10623      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10624      */
10625     invalidText : "",
10626     
10627     
10628     
10629     autocomplete: false,
10630     
10631     
10632     fieldLabel : '',
10633     inputType : 'text',
10634     
10635     name : false,
10636     placeholder: false,
10637     before : false,
10638     after : false,
10639     size : false,
10640     hasFocus : false,
10641     preventMark: false,
10642     isFormField : true,
10643     value : '',
10644     labelWidth : 2,
10645     labelAlign : false,
10646     readOnly : false,
10647     align : false,
10648     formatedValue : false,
10649     forceFeedback : false,
10650     
10651     indicatorpos : 'left',
10652     
10653     labellg : 0,
10654     labelmd : 0,
10655     labelsm : 0,
10656     labelxs : 0,
10657     
10658     capture : '',
10659     accept : '',
10660     
10661     parentLabelAlign : function()
10662     {
10663         var parent = this;
10664         while (parent.parent()) {
10665             parent = parent.parent();
10666             if (typeof(parent.labelAlign) !='undefined') {
10667                 return parent.labelAlign;
10668             }
10669         }
10670         return 'left';
10671         
10672     },
10673     
10674     getAutoCreate : function()
10675     {
10676         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10677         
10678         var id = Roo.id();
10679         
10680         var cfg = {};
10681         
10682         if(this.inputType != 'hidden'){
10683             cfg.cls = 'form-group' //input-group
10684         }
10685         
10686         var input =  {
10687             tag: 'input',
10688             id : id,
10689             type : this.inputType,
10690             value : this.value,
10691             cls : 'form-control',
10692             placeholder : this.placeholder || '',
10693             autocomplete : this.autocomplete || 'new-password'
10694         };
10695         if (this.inputType == 'file') {
10696             input.style = 'overflow:hidden'; // why not in CSS?
10697         }
10698         
10699         if(this.capture.length){
10700             input.capture = this.capture;
10701         }
10702         
10703         if(this.accept.length){
10704             input.accept = this.accept + "/*";
10705         }
10706         
10707         if(this.align){
10708             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10709         }
10710         
10711         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10712             input.maxLength = this.maxLength;
10713         }
10714         
10715         if (this.disabled) {
10716             input.disabled=true;
10717         }
10718         
10719         if (this.readOnly) {
10720             input.readonly=true;
10721         }
10722         
10723         if (this.name) {
10724             input.name = this.name;
10725         }
10726         
10727         if (this.size) {
10728             input.cls += ' input-' + this.size;
10729         }
10730         
10731         var settings=this;
10732         ['xs','sm','md','lg'].map(function(size){
10733             if (settings[size]) {
10734                 cfg.cls += ' col-' + size + '-' + settings[size];
10735             }
10736         });
10737         
10738         var inputblock = input;
10739         
10740         var feedback = {
10741             tag: 'span',
10742             cls: 'glyphicon form-control-feedback'
10743         };
10744             
10745         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10746             
10747             inputblock = {
10748                 cls : 'has-feedback',
10749                 cn :  [
10750                     input,
10751                     feedback
10752                 ] 
10753             };  
10754         }
10755         
10756         if (this.before || this.after) {
10757             
10758             inputblock = {
10759                 cls : 'input-group',
10760                 cn :  [] 
10761             };
10762             
10763             if (this.before && typeof(this.before) == 'string') {
10764                 
10765                 inputblock.cn.push({
10766                     tag :'span',
10767                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10768                     html : this.before
10769                 });
10770             }
10771             if (this.before && typeof(this.before) == 'object') {
10772                 this.before = Roo.factory(this.before);
10773                 
10774                 inputblock.cn.push({
10775                     tag :'span',
10776                     cls : 'roo-input-before input-group-prepend   input-group-' +
10777                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10778                 });
10779             }
10780             
10781             inputblock.cn.push(input);
10782             
10783             if (this.after && typeof(this.after) == 'string') {
10784                 inputblock.cn.push({
10785                     tag :'span',
10786                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10787                     html : this.after
10788                 });
10789             }
10790             if (this.after && typeof(this.after) == 'object') {
10791                 this.after = Roo.factory(this.after);
10792                 
10793                 inputblock.cn.push({
10794                     tag :'span',
10795                     cls : 'roo-input-after input-group-append  input-group-' +
10796                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10797                 });
10798             }
10799             
10800             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10801                 inputblock.cls += ' has-feedback';
10802                 inputblock.cn.push(feedback);
10803             }
10804         };
10805         var indicator = {
10806             tag : 'i',
10807             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10808             tooltip : 'This field is required'
10809         };
10810         if (this.allowBlank ) {
10811             indicator.style = this.allowBlank ? ' display:none' : '';
10812         }
10813         if (align ==='left' && this.fieldLabel.length) {
10814             
10815             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10816             
10817             cfg.cn = [
10818                 indicator,
10819                 {
10820                     tag: 'label',
10821                     'for' :  id,
10822                     cls : 'control-label col-form-label',
10823                     html : this.fieldLabel
10824
10825                 },
10826                 {
10827                     cls : "", 
10828                     cn: [
10829                         inputblock
10830                     ]
10831                 }
10832             ];
10833             
10834             var labelCfg = cfg.cn[1];
10835             var contentCfg = cfg.cn[2];
10836             
10837             if(this.indicatorpos == 'right'){
10838                 cfg.cn = [
10839                     {
10840                         tag: 'label',
10841                         'for' :  id,
10842                         cls : 'control-label col-form-label',
10843                         cn : [
10844                             {
10845                                 tag : 'span',
10846                                 html : this.fieldLabel
10847                             },
10848                             indicator
10849                         ]
10850                     },
10851                     {
10852                         cls : "",
10853                         cn: [
10854                             inputblock
10855                         ]
10856                     }
10857
10858                 ];
10859                 
10860                 labelCfg = cfg.cn[0];
10861                 contentCfg = cfg.cn[1];
10862             
10863             }
10864             
10865             if(this.labelWidth > 12){
10866                 labelCfg.style = "width: " + this.labelWidth + 'px';
10867             }
10868             
10869             if(this.labelWidth < 13 && this.labelmd == 0){
10870                 this.labelmd = this.labelWidth;
10871             }
10872             
10873             if(this.labellg > 0){
10874                 labelCfg.cls += ' col-lg-' + this.labellg;
10875                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10876             }
10877             
10878             if(this.labelmd > 0){
10879                 labelCfg.cls += ' col-md-' + this.labelmd;
10880                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10881             }
10882             
10883             if(this.labelsm > 0){
10884                 labelCfg.cls += ' col-sm-' + this.labelsm;
10885                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10886             }
10887             
10888             if(this.labelxs > 0){
10889                 labelCfg.cls += ' col-xs-' + this.labelxs;
10890                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10891             }
10892             
10893             
10894         } else if ( this.fieldLabel.length) {
10895                 
10896             
10897             
10898             cfg.cn = [
10899                 {
10900                     tag : 'i',
10901                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10902                     tooltip : 'This field is required',
10903                     style : this.allowBlank ? ' display:none' : '' 
10904                 },
10905                 {
10906                     tag: 'label',
10907                    //cls : 'input-group-addon',
10908                     html : this.fieldLabel
10909
10910                 },
10911
10912                inputblock
10913
10914            ];
10915            
10916            if(this.indicatorpos == 'right'){
10917        
10918                 cfg.cn = [
10919                     {
10920                         tag: 'label',
10921                        //cls : 'input-group-addon',
10922                         html : this.fieldLabel
10923
10924                     },
10925                     {
10926                         tag : 'i',
10927                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10928                         tooltip : 'This field is required',
10929                         style : this.allowBlank ? ' display:none' : '' 
10930                     },
10931
10932                    inputblock
10933
10934                ];
10935
10936             }
10937
10938         } else {
10939             
10940             cfg.cn = [
10941
10942                     inputblock
10943
10944             ];
10945                 
10946                 
10947         };
10948         
10949         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10950            cfg.cls += ' navbar-form';
10951         }
10952         
10953         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10954             // on BS4 we do this only if not form 
10955             cfg.cls += ' navbar-form';
10956             cfg.tag = 'li';
10957         }
10958         
10959         return cfg;
10960         
10961     },
10962     /**
10963      * return the real input element.
10964      */
10965     inputEl: function ()
10966     {
10967         return this.el.select('input.form-control',true).first();
10968     },
10969     
10970     tooltipEl : function()
10971     {
10972         return this.inputEl();
10973     },
10974     
10975     indicatorEl : function()
10976     {
10977         if (Roo.bootstrap.version == 4) {
10978             return false; // not enabled in v4 yet.
10979         }
10980         
10981         var indicator = this.el.select('i.roo-required-indicator',true).first();
10982         
10983         if(!indicator){
10984             return false;
10985         }
10986         
10987         return indicator;
10988         
10989     },
10990     
10991     setDisabled : function(v)
10992     {
10993         var i  = this.inputEl().dom;
10994         if (!v) {
10995             i.removeAttribute('disabled');
10996             return;
10997             
10998         }
10999         i.setAttribute('disabled','true');
11000     },
11001     initEvents : function()
11002     {
11003           
11004         this.inputEl().on("keydown" , this.fireKey,  this);
11005         this.inputEl().on("focus", this.onFocus,  this);
11006         this.inputEl().on("blur", this.onBlur,  this);
11007         
11008         this.inputEl().relayEvent('keyup', this);
11009         this.inputEl().relayEvent('paste', this);
11010         
11011         this.indicator = this.indicatorEl();
11012         
11013         if(this.indicator){
11014             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11015         }
11016  
11017         // reference to original value for reset
11018         this.originalValue = this.getValue();
11019         //Roo.form.TextField.superclass.initEvents.call(this);
11020         if(this.validationEvent == 'keyup'){
11021             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11022             this.inputEl().on('keyup', this.filterValidation, this);
11023         }
11024         else if(this.validationEvent !== false){
11025             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11026         }
11027         
11028         if(this.selectOnFocus){
11029             this.on("focus", this.preFocus, this);
11030             
11031         }
11032         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11033             this.inputEl().on("keypress", this.filterKeys, this);
11034         } else {
11035             this.inputEl().relayEvent('keypress', this);
11036         }
11037        /* if(this.grow){
11038             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11039             this.el.on("click", this.autoSize,  this);
11040         }
11041         */
11042         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11043             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11044         }
11045         
11046         if (typeof(this.before) == 'object') {
11047             this.before.render(this.el.select('.roo-input-before',true).first());
11048         }
11049         if (typeof(this.after) == 'object') {
11050             this.after.render(this.el.select('.roo-input-after',true).first());
11051         }
11052         
11053         this.inputEl().on('change', this.onChange, this);
11054         
11055     },
11056     filterValidation : function(e){
11057         if(!e.isNavKeyPress()){
11058             this.validationTask.delay(this.validationDelay);
11059         }
11060     },
11061      /**
11062      * Validates the field value
11063      * @return {Boolean} True if the value is valid, else false
11064      */
11065     validate : function(){
11066         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11067         if(this.disabled || this.validateValue(this.getRawValue())){
11068             this.markValid();
11069             return true;
11070         }
11071         
11072         this.markInvalid();
11073         return false;
11074     },
11075     
11076     
11077     /**
11078      * Validates a value according to the field's validation rules and marks the field as invalid
11079      * if the validation fails
11080      * @param {Mixed} value The value to validate
11081      * @return {Boolean} True if the value is valid, else false
11082      */
11083     validateValue : function(value)
11084     {
11085         if(this.getVisibilityEl().hasClass('hidden')){
11086             return true;
11087         }
11088         
11089         if(value.length < 1)  { // if it's blank
11090             if(this.allowBlank){
11091                 return true;
11092             }
11093             return false;
11094         }
11095         
11096         if(value.length < this.minLength){
11097             return false;
11098         }
11099         if(value.length > this.maxLength){
11100             return false;
11101         }
11102         if(this.vtype){
11103             var vt = Roo.form.VTypes;
11104             if(!vt[this.vtype](value, this)){
11105                 return false;
11106             }
11107         }
11108         if(typeof this.validator == "function"){
11109             var msg = this.validator(value);
11110             if(msg !== true){
11111                 return false;
11112             }
11113             if (typeof(msg) == 'string') {
11114                 this.invalidText = msg;
11115             }
11116         }
11117         
11118         if(this.regex && !this.regex.test(value)){
11119             return false;
11120         }
11121         
11122         return true;
11123     },
11124     
11125      // private
11126     fireKey : function(e){
11127         //Roo.log('field ' + e.getKey());
11128         if(e.isNavKeyPress()){
11129             this.fireEvent("specialkey", this, e);
11130         }
11131     },
11132     focus : function (selectText){
11133         if(this.rendered){
11134             this.inputEl().focus();
11135             if(selectText === true){
11136                 this.inputEl().dom.select();
11137             }
11138         }
11139         return this;
11140     } ,
11141     
11142     onFocus : function(){
11143         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11144            // this.el.addClass(this.focusClass);
11145         }
11146         if(!this.hasFocus){
11147             this.hasFocus = true;
11148             this.startValue = this.getValue();
11149             this.fireEvent("focus", this);
11150         }
11151     },
11152     
11153     beforeBlur : Roo.emptyFn,
11154
11155     
11156     // private
11157     onBlur : function(){
11158         this.beforeBlur();
11159         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11160             //this.el.removeClass(this.focusClass);
11161         }
11162         this.hasFocus = false;
11163         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11164             this.validate();
11165         }
11166         var v = this.getValue();
11167         if(String(v) !== String(this.startValue)){
11168             this.fireEvent('change', this, v, this.startValue);
11169         }
11170         this.fireEvent("blur", this);
11171     },
11172     
11173     onChange : function(e)
11174     {
11175         var v = this.getValue();
11176         if(String(v) !== String(this.startValue)){
11177             this.fireEvent('change', this, v, this.startValue);
11178         }
11179         
11180     },
11181     
11182     /**
11183      * Resets the current field value to the originally loaded value and clears any validation messages
11184      */
11185     reset : function(){
11186         this.setValue(this.originalValue);
11187         this.validate();
11188     },
11189      /**
11190      * Returns the name of the field
11191      * @return {Mixed} name The name field
11192      */
11193     getName: function(){
11194         return this.name;
11195     },
11196      /**
11197      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11198      * @return {Mixed} value The field value
11199      */
11200     getValue : function(){
11201         
11202         var v = this.inputEl().getValue();
11203         
11204         return v;
11205     },
11206     /**
11207      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11208      * @return {Mixed} value The field value
11209      */
11210     getRawValue : function(){
11211         var v = this.inputEl().getValue();
11212         
11213         return v;
11214     },
11215     
11216     /**
11217      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11218      * @param {Mixed} value The value to set
11219      */
11220     setRawValue : function(v){
11221         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11222     },
11223     
11224     selectText : function(start, end){
11225         var v = this.getRawValue();
11226         if(v.length > 0){
11227             start = start === undefined ? 0 : start;
11228             end = end === undefined ? v.length : end;
11229             var d = this.inputEl().dom;
11230             if(d.setSelectionRange){
11231                 d.setSelectionRange(start, end);
11232             }else if(d.createTextRange){
11233                 var range = d.createTextRange();
11234                 range.moveStart("character", start);
11235                 range.moveEnd("character", v.length-end);
11236                 range.select();
11237             }
11238         }
11239     },
11240     
11241     /**
11242      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11243      * @param {Mixed} value The value to set
11244      */
11245     setValue : function(v){
11246         this.value = v;
11247         if(this.rendered){
11248             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11249             this.validate();
11250         }
11251     },
11252     
11253     /*
11254     processValue : function(value){
11255         if(this.stripCharsRe){
11256             var newValue = value.replace(this.stripCharsRe, '');
11257             if(newValue !== value){
11258                 this.setRawValue(newValue);
11259                 return newValue;
11260             }
11261         }
11262         return value;
11263     },
11264   */
11265     preFocus : function(){
11266         
11267         if(this.selectOnFocus){
11268             this.inputEl().dom.select();
11269         }
11270     },
11271     filterKeys : function(e){
11272         var k = e.getKey();
11273         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11274             return;
11275         }
11276         var c = e.getCharCode(), cc = String.fromCharCode(c);
11277         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11278             return;
11279         }
11280         if(!this.maskRe.test(cc)){
11281             e.stopEvent();
11282         }
11283     },
11284      /**
11285      * Clear any invalid styles/messages for this field
11286      */
11287     clearInvalid : function(){
11288         
11289         if(!this.el || this.preventMark){ // not rendered
11290             return;
11291         }
11292         
11293         
11294         this.el.removeClass([this.invalidClass, 'is-invalid']);
11295         
11296         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11297             
11298             var feedback = this.el.select('.form-control-feedback', true).first();
11299             
11300             if(feedback){
11301                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11302             }
11303             
11304         }
11305         
11306         if(this.indicator){
11307             this.indicator.removeClass('visible');
11308             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11309         }
11310         
11311         this.fireEvent('valid', this);
11312     },
11313     
11314      /**
11315      * Mark this field as valid
11316      */
11317     markValid : function()
11318     {
11319         if(!this.el  || this.preventMark){ // not rendered...
11320             return;
11321         }
11322         
11323         this.el.removeClass([this.invalidClass, this.validClass]);
11324         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11325
11326         var feedback = this.el.select('.form-control-feedback', true).first();
11327             
11328         if(feedback){
11329             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11330         }
11331         
11332         if(this.indicator){
11333             this.indicator.removeClass('visible');
11334             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11335         }
11336         
11337         if(this.disabled){
11338             return;
11339         }
11340         
11341            
11342         if(this.allowBlank && !this.getRawValue().length){
11343             return;
11344         }
11345         if (Roo.bootstrap.version == 3) {
11346             this.el.addClass(this.validClass);
11347         } else {
11348             this.inputEl().addClass('is-valid');
11349         }
11350
11351         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11352             
11353             var feedback = this.el.select('.form-control-feedback', true).first();
11354             
11355             if(feedback){
11356                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11357                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11358             }
11359             
11360         }
11361         
11362         this.fireEvent('valid', this);
11363     },
11364     
11365      /**
11366      * Mark this field as invalid
11367      * @param {String} msg The validation message
11368      */
11369     markInvalid : function(msg)
11370     {
11371         if(!this.el  || this.preventMark){ // not rendered
11372             return;
11373         }
11374         
11375         this.el.removeClass([this.invalidClass, this.validClass]);
11376         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11377         
11378         var feedback = this.el.select('.form-control-feedback', true).first();
11379             
11380         if(feedback){
11381             this.el.select('.form-control-feedback', true).first().removeClass(
11382                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11383         }
11384
11385         if(this.disabled){
11386             return;
11387         }
11388         
11389         if(this.allowBlank && !this.getRawValue().length){
11390             return;
11391         }
11392         
11393         if(this.indicator){
11394             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11395             this.indicator.addClass('visible');
11396         }
11397         if (Roo.bootstrap.version == 3) {
11398             this.el.addClass(this.invalidClass);
11399         } else {
11400             this.inputEl().addClass('is-invalid');
11401         }
11402         
11403         
11404         
11405         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11406             
11407             var feedback = this.el.select('.form-control-feedback', true).first();
11408             
11409             if(feedback){
11410                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11411                 
11412                 if(this.getValue().length || this.forceFeedback){
11413                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11414                 }
11415                 
11416             }
11417             
11418         }
11419         
11420         this.fireEvent('invalid', this, msg);
11421     },
11422     // private
11423     SafariOnKeyDown : function(event)
11424     {
11425         // this is a workaround for a password hang bug on chrome/ webkit.
11426         if (this.inputEl().dom.type != 'password') {
11427             return;
11428         }
11429         
11430         var isSelectAll = false;
11431         
11432         if(this.inputEl().dom.selectionEnd > 0){
11433             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11434         }
11435         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11436             event.preventDefault();
11437             this.setValue('');
11438             return;
11439         }
11440         
11441         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11442             
11443             event.preventDefault();
11444             // this is very hacky as keydown always get's upper case.
11445             //
11446             var cc = String.fromCharCode(event.getCharCode());
11447             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11448             
11449         }
11450     },
11451     adjustWidth : function(tag, w){
11452         tag = tag.toLowerCase();
11453         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11454             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11455                 if(tag == 'input'){
11456                     return w + 2;
11457                 }
11458                 if(tag == 'textarea'){
11459                     return w-2;
11460                 }
11461             }else if(Roo.isOpera){
11462                 if(tag == 'input'){
11463                     return w + 2;
11464                 }
11465                 if(tag == 'textarea'){
11466                     return w-2;
11467                 }
11468             }
11469         }
11470         return w;
11471     },
11472     
11473     setFieldLabel : function(v)
11474     {
11475         if(!this.rendered){
11476             return;
11477         }
11478         
11479         if(this.indicatorEl()){
11480             var ar = this.el.select('label > span',true);
11481             
11482             if (ar.elements.length) {
11483                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11484                 this.fieldLabel = v;
11485                 return;
11486             }
11487             
11488             var br = this.el.select('label',true);
11489             
11490             if(br.elements.length) {
11491                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11492                 this.fieldLabel = v;
11493                 return;
11494             }
11495             
11496             Roo.log('Cannot Found any of label > span || label in input');
11497             return;
11498         }
11499         
11500         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11501         this.fieldLabel = v;
11502         
11503         
11504     }
11505 });
11506
11507  
11508 /*
11509  * - LGPL
11510  *
11511  * Input
11512  * 
11513  */
11514
11515 /**
11516  * @class Roo.bootstrap.TextArea
11517  * @extends Roo.bootstrap.Input
11518  * Bootstrap TextArea class
11519  * @cfg {Number} cols Specifies the visible width of a text area
11520  * @cfg {Number} rows Specifies the visible number of lines in a text area
11521  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11522  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11523  * @cfg {string} html text
11524  * 
11525  * @constructor
11526  * Create a new TextArea
11527  * @param {Object} config The config object
11528  */
11529
11530 Roo.bootstrap.TextArea = function(config){
11531     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11532    
11533 };
11534
11535 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11536      
11537     cols : false,
11538     rows : 5,
11539     readOnly : false,
11540     warp : 'soft',
11541     resize : false,
11542     value: false,
11543     html: false,
11544     
11545     getAutoCreate : function(){
11546         
11547         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11548         
11549         var id = Roo.id();
11550         
11551         var cfg = {};
11552         
11553         if(this.inputType != 'hidden'){
11554             cfg.cls = 'form-group' //input-group
11555         }
11556         
11557         var input =  {
11558             tag: 'textarea',
11559             id : id,
11560             warp : this.warp,
11561             rows : this.rows,
11562             value : this.value || '',
11563             html: this.html || '',
11564             cls : 'form-control',
11565             placeholder : this.placeholder || '' 
11566             
11567         };
11568         
11569         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11570             input.maxLength = this.maxLength;
11571         }
11572         
11573         if(this.resize){
11574             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11575         }
11576         
11577         if(this.cols){
11578             input.cols = this.cols;
11579         }
11580         
11581         if (this.readOnly) {
11582             input.readonly = true;
11583         }
11584         
11585         if (this.name) {
11586             input.name = this.name;
11587         }
11588         
11589         if (this.size) {
11590             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11591         }
11592         
11593         var settings=this;
11594         ['xs','sm','md','lg'].map(function(size){
11595             if (settings[size]) {
11596                 cfg.cls += ' col-' + size + '-' + settings[size];
11597             }
11598         });
11599         
11600         var inputblock = input;
11601         
11602         if(this.hasFeedback && !this.allowBlank){
11603             
11604             var feedback = {
11605                 tag: 'span',
11606                 cls: 'glyphicon form-control-feedback'
11607             };
11608
11609             inputblock = {
11610                 cls : 'has-feedback',
11611                 cn :  [
11612                     input,
11613                     feedback
11614                 ] 
11615             };  
11616         }
11617         
11618         
11619         if (this.before || this.after) {
11620             
11621             inputblock = {
11622                 cls : 'input-group',
11623                 cn :  [] 
11624             };
11625             if (this.before) {
11626                 inputblock.cn.push({
11627                     tag :'span',
11628                     cls : 'input-group-addon',
11629                     html : this.before
11630                 });
11631             }
11632             
11633             inputblock.cn.push(input);
11634             
11635             if(this.hasFeedback && !this.allowBlank){
11636                 inputblock.cls += ' has-feedback';
11637                 inputblock.cn.push(feedback);
11638             }
11639             
11640             if (this.after) {
11641                 inputblock.cn.push({
11642                     tag :'span',
11643                     cls : 'input-group-addon',
11644                     html : this.after
11645                 });
11646             }
11647             
11648         }
11649         
11650         if (align ==='left' && this.fieldLabel.length) {
11651             cfg.cn = [
11652                 {
11653                     tag: 'label',
11654                     'for' :  id,
11655                     cls : 'control-label',
11656                     html : this.fieldLabel
11657                 },
11658                 {
11659                     cls : "",
11660                     cn: [
11661                         inputblock
11662                     ]
11663                 }
11664
11665             ];
11666             
11667             if(this.labelWidth > 12){
11668                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11669             }
11670
11671             if(this.labelWidth < 13 && this.labelmd == 0){
11672                 this.labelmd = this.labelWidth;
11673             }
11674
11675             if(this.labellg > 0){
11676                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11677                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11678             }
11679
11680             if(this.labelmd > 0){
11681                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11682                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11683             }
11684
11685             if(this.labelsm > 0){
11686                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11687                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11688             }
11689
11690             if(this.labelxs > 0){
11691                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11692                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11693             }
11694             
11695         } else if ( this.fieldLabel.length) {
11696             cfg.cn = [
11697
11698                {
11699                    tag: 'label',
11700                    //cls : 'input-group-addon',
11701                    html : this.fieldLabel
11702
11703                },
11704
11705                inputblock
11706
11707            ];
11708
11709         } else {
11710
11711             cfg.cn = [
11712
11713                 inputblock
11714
11715             ];
11716                 
11717         }
11718         
11719         if (this.disabled) {
11720             input.disabled=true;
11721         }
11722         
11723         return cfg;
11724         
11725     },
11726     /**
11727      * return the real textarea element.
11728      */
11729     inputEl: function ()
11730     {
11731         return this.el.select('textarea.form-control',true).first();
11732     },
11733     
11734     /**
11735      * Clear any invalid styles/messages for this field
11736      */
11737     clearInvalid : function()
11738     {
11739         
11740         if(!this.el || this.preventMark){ // not rendered
11741             return;
11742         }
11743         
11744         var label = this.el.select('label', true).first();
11745         var icon = this.el.select('i.fa-star', true).first();
11746         
11747         if(label && icon){
11748             icon.remove();
11749         }
11750         this.el.removeClass( this.validClass);
11751         this.inputEl().removeClass('is-invalid');
11752          
11753         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11754             
11755             var feedback = this.el.select('.form-control-feedback', true).first();
11756             
11757             if(feedback){
11758                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11759             }
11760             
11761         }
11762         
11763         this.fireEvent('valid', this);
11764     },
11765     
11766      /**
11767      * Mark this field as valid
11768      */
11769     markValid : function()
11770     {
11771         if(!this.el  || this.preventMark){ // not rendered
11772             return;
11773         }
11774         
11775         this.el.removeClass([this.invalidClass, this.validClass]);
11776         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11777         
11778         var feedback = this.el.select('.form-control-feedback', true).first();
11779             
11780         if(feedback){
11781             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11782         }
11783
11784         if(this.disabled || this.allowBlank){
11785             return;
11786         }
11787         
11788         var label = this.el.select('label', true).first();
11789         var icon = this.el.select('i.fa-star', true).first();
11790         
11791         if(label && icon){
11792             icon.remove();
11793         }
11794         if (Roo.bootstrap.version == 3) {
11795             this.el.addClass(this.validClass);
11796         } else {
11797             this.inputEl().addClass('is-valid');
11798         }
11799         
11800         
11801         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11802             
11803             var feedback = this.el.select('.form-control-feedback', true).first();
11804             
11805             if(feedback){
11806                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11807                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11808             }
11809             
11810         }
11811         
11812         this.fireEvent('valid', this);
11813     },
11814     
11815      /**
11816      * Mark this field as invalid
11817      * @param {String} msg The validation message
11818      */
11819     markInvalid : function(msg)
11820     {
11821         if(!this.el  || this.preventMark){ // not rendered
11822             return;
11823         }
11824         
11825         this.el.removeClass([this.invalidClass, this.validClass]);
11826         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11827         
11828         var feedback = this.el.select('.form-control-feedback', true).first();
11829             
11830         if(feedback){
11831             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11832         }
11833
11834         if(this.disabled || this.allowBlank){
11835             return;
11836         }
11837         
11838         var label = this.el.select('label', true).first();
11839         var icon = this.el.select('i.fa-star', true).first();
11840         
11841         if(!this.getValue().length && label && !icon){
11842             this.el.createChild({
11843                 tag : 'i',
11844                 cls : 'text-danger fa fa-lg fa-star',
11845                 tooltip : 'This field is required',
11846                 style : 'margin-right:5px;'
11847             }, label, true);
11848         }
11849         
11850         if (Roo.bootstrap.version == 3) {
11851             this.el.addClass(this.invalidClass);
11852         } else {
11853             this.inputEl().addClass('is-invalid');
11854         }
11855         
11856         // fixme ... this may be depricated need to test..
11857         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11858             
11859             var feedback = this.el.select('.form-control-feedback', true).first();
11860             
11861             if(feedback){
11862                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11863                 
11864                 if(this.getValue().length || this.forceFeedback){
11865                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11866                 }
11867                 
11868             }
11869             
11870         }
11871         
11872         this.fireEvent('invalid', this, msg);
11873     }
11874 });
11875
11876  
11877 /*
11878  * - LGPL
11879  *
11880  * trigger field - base class for combo..
11881  * 
11882  */
11883  
11884 /**
11885  * @class Roo.bootstrap.TriggerField
11886  * @extends Roo.bootstrap.Input
11887  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11888  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11889  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11890  * for which you can provide a custom implementation.  For example:
11891  * <pre><code>
11892 var trigger = new Roo.bootstrap.TriggerField();
11893 trigger.onTriggerClick = myTriggerFn;
11894 trigger.applyTo('my-field');
11895 </code></pre>
11896  *
11897  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11898  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11899  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11900  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11901  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11902
11903  * @constructor
11904  * Create a new TriggerField.
11905  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11906  * to the base TextField)
11907  */
11908 Roo.bootstrap.TriggerField = function(config){
11909     this.mimicing = false;
11910     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11911 };
11912
11913 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11914     /**
11915      * @cfg {String} triggerClass A CSS class to apply to the trigger
11916      */
11917      /**
11918      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11919      */
11920     hideTrigger:false,
11921
11922     /**
11923      * @cfg {Boolean} removable (true|false) special filter default false
11924      */
11925     removable : false,
11926     
11927     /** @cfg {Boolean} grow @hide */
11928     /** @cfg {Number} growMin @hide */
11929     /** @cfg {Number} growMax @hide */
11930
11931     /**
11932      * @hide 
11933      * @method
11934      */
11935     autoSize: Roo.emptyFn,
11936     // private
11937     monitorTab : true,
11938     // private
11939     deferHeight : true,
11940
11941     
11942     actionMode : 'wrap',
11943     
11944     caret : false,
11945     
11946     
11947     getAutoCreate : function(){
11948        
11949         var align = this.labelAlign || this.parentLabelAlign();
11950         
11951         var id = Roo.id();
11952         
11953         var cfg = {
11954             cls: 'form-group' //input-group
11955         };
11956         
11957         
11958         var input =  {
11959             tag: 'input',
11960             id : id,
11961             type : this.inputType,
11962             cls : 'form-control',
11963             autocomplete: 'new-password',
11964             placeholder : this.placeholder || '' 
11965             
11966         };
11967         if (this.name) {
11968             input.name = this.name;
11969         }
11970         if (this.size) {
11971             input.cls += ' input-' + this.size;
11972         }
11973         
11974         if (this.disabled) {
11975             input.disabled=true;
11976         }
11977         
11978         var inputblock = input;
11979         
11980         if(this.hasFeedback && !this.allowBlank){
11981             
11982             var feedback = {
11983                 tag: 'span',
11984                 cls: 'glyphicon form-control-feedback'
11985             };
11986             
11987             if(this.removable && !this.editable  ){
11988                 inputblock = {
11989                     cls : 'has-feedback',
11990                     cn :  [
11991                         inputblock,
11992                         {
11993                             tag: 'button',
11994                             html : 'x',
11995                             cls : 'roo-combo-removable-btn close'
11996                         },
11997                         feedback
11998                     ] 
11999                 };
12000             } else {
12001                 inputblock = {
12002                     cls : 'has-feedback',
12003                     cn :  [
12004                         inputblock,
12005                         feedback
12006                     ] 
12007                 };
12008             }
12009
12010         } else {
12011             if(this.removable && !this.editable ){
12012                 inputblock = {
12013                     cls : 'roo-removable',
12014                     cn :  [
12015                         inputblock,
12016                         {
12017                             tag: 'button',
12018                             html : 'x',
12019                             cls : 'roo-combo-removable-btn close'
12020                         }
12021                     ] 
12022                 };
12023             }
12024         }
12025         
12026         if (this.before || this.after) {
12027             
12028             inputblock = {
12029                 cls : 'input-group',
12030                 cn :  [] 
12031             };
12032             if (this.before) {
12033                 inputblock.cn.push({
12034                     tag :'span',
12035                     cls : 'input-group-addon input-group-prepend input-group-text',
12036                     html : this.before
12037                 });
12038             }
12039             
12040             inputblock.cn.push(input);
12041             
12042             if(this.hasFeedback && !this.allowBlank){
12043                 inputblock.cls += ' has-feedback';
12044                 inputblock.cn.push(feedback);
12045             }
12046             
12047             if (this.after) {
12048                 inputblock.cn.push({
12049                     tag :'span',
12050                     cls : 'input-group-addon input-group-append input-group-text',
12051                     html : this.after
12052                 });
12053             }
12054             
12055         };
12056         
12057       
12058         
12059         var ibwrap = inputblock;
12060         
12061         if(this.multiple){
12062             ibwrap = {
12063                 tag: 'ul',
12064                 cls: 'roo-select2-choices',
12065                 cn:[
12066                     {
12067                         tag: 'li',
12068                         cls: 'roo-select2-search-field',
12069                         cn: [
12070
12071                             inputblock
12072                         ]
12073                     }
12074                 ]
12075             };
12076                 
12077         }
12078         
12079         var combobox = {
12080             cls: 'roo-select2-container input-group',
12081             cn: [
12082                  {
12083                     tag: 'input',
12084                     type : 'hidden',
12085                     cls: 'form-hidden-field'
12086                 },
12087                 ibwrap
12088             ]
12089         };
12090         
12091         if(!this.multiple && this.showToggleBtn){
12092             
12093             var caret = {
12094                         tag: 'span',
12095                         cls: 'caret'
12096              };
12097             if (this.caret != false) {
12098                 caret = {
12099                      tag: 'i',
12100                      cls: 'fa fa-' + this.caret
12101                 };
12102                 
12103             }
12104             
12105             combobox.cn.push({
12106                 tag :'span',
12107                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12108                 cn : [
12109                     Roo.bootstrap.version == 3 ? caret : '',
12110                     {
12111                         tag: 'span',
12112                         cls: 'combobox-clear',
12113                         cn  : [
12114                             {
12115                                 tag : 'i',
12116                                 cls: 'icon-remove'
12117                             }
12118                         ]
12119                     }
12120                 ]
12121
12122             })
12123         }
12124         
12125         if(this.multiple){
12126             combobox.cls += ' roo-select2-container-multi';
12127         }
12128          var indicator = {
12129             tag : 'i',
12130             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12131             tooltip : 'This field is required'
12132         };
12133         if (Roo.bootstrap.version == 4) {
12134             indicator = {
12135                 tag : 'i',
12136                 style : 'display:none'
12137             };
12138         }
12139         
12140         
12141         if (align ==='left' && this.fieldLabel.length) {
12142             
12143             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12144
12145             cfg.cn = [
12146                 indicator,
12147                 {
12148                     tag: 'label',
12149                     'for' :  id,
12150                     cls : 'control-label',
12151                     html : this.fieldLabel
12152
12153                 },
12154                 {
12155                     cls : "", 
12156                     cn: [
12157                         combobox
12158                     ]
12159                 }
12160
12161             ];
12162             
12163             var labelCfg = cfg.cn[1];
12164             var contentCfg = cfg.cn[2];
12165             
12166             if(this.indicatorpos == 'right'){
12167                 cfg.cn = [
12168                     {
12169                         tag: 'label',
12170                         'for' :  id,
12171                         cls : 'control-label',
12172                         cn : [
12173                             {
12174                                 tag : 'span',
12175                                 html : this.fieldLabel
12176                             },
12177                             indicator
12178                         ]
12179                     },
12180                     {
12181                         cls : "", 
12182                         cn: [
12183                             combobox
12184                         ]
12185                     }
12186
12187                 ];
12188                 
12189                 labelCfg = cfg.cn[0];
12190                 contentCfg = cfg.cn[1];
12191             }
12192             
12193             if(this.labelWidth > 12){
12194                 labelCfg.style = "width: " + this.labelWidth + 'px';
12195             }
12196             
12197             if(this.labelWidth < 13 && this.labelmd == 0){
12198                 this.labelmd = this.labelWidth;
12199             }
12200             
12201             if(this.labellg > 0){
12202                 labelCfg.cls += ' col-lg-' + this.labellg;
12203                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12204             }
12205             
12206             if(this.labelmd > 0){
12207                 labelCfg.cls += ' col-md-' + this.labelmd;
12208                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12209             }
12210             
12211             if(this.labelsm > 0){
12212                 labelCfg.cls += ' col-sm-' + this.labelsm;
12213                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12214             }
12215             
12216             if(this.labelxs > 0){
12217                 labelCfg.cls += ' col-xs-' + this.labelxs;
12218                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12219             }
12220             
12221         } else if ( this.fieldLabel.length) {
12222 //                Roo.log(" label");
12223             cfg.cn = [
12224                 indicator,
12225                {
12226                    tag: 'label',
12227                    //cls : 'input-group-addon',
12228                    html : this.fieldLabel
12229
12230                },
12231
12232                combobox
12233
12234             ];
12235             
12236             if(this.indicatorpos == 'right'){
12237                 
12238                 cfg.cn = [
12239                     {
12240                        tag: 'label',
12241                        cn : [
12242                            {
12243                                tag : 'span',
12244                                html : this.fieldLabel
12245                            },
12246                            indicator
12247                        ]
12248
12249                     },
12250                     combobox
12251
12252                 ];
12253
12254             }
12255
12256         } else {
12257             
12258 //                Roo.log(" no label && no align");
12259                 cfg = combobox
12260                      
12261                 
12262         }
12263         
12264         var settings=this;
12265         ['xs','sm','md','lg'].map(function(size){
12266             if (settings[size]) {
12267                 cfg.cls += ' col-' + size + '-' + settings[size];
12268             }
12269         });
12270         
12271         return cfg;
12272         
12273     },
12274     
12275     
12276     
12277     // private
12278     onResize : function(w, h){
12279 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12280 //        if(typeof w == 'number'){
12281 //            var x = w - this.trigger.getWidth();
12282 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12283 //            this.trigger.setStyle('left', x+'px');
12284 //        }
12285     },
12286
12287     // private
12288     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12289
12290     // private
12291     getResizeEl : function(){
12292         return this.inputEl();
12293     },
12294
12295     // private
12296     getPositionEl : function(){
12297         return this.inputEl();
12298     },
12299
12300     // private
12301     alignErrorIcon : function(){
12302         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12303     },
12304
12305     // private
12306     initEvents : function(){
12307         
12308         this.createList();
12309         
12310         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12311         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12312         if(!this.multiple && this.showToggleBtn){
12313             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12314             if(this.hideTrigger){
12315                 this.trigger.setDisplayed(false);
12316             }
12317             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12318         }
12319         
12320         if(this.multiple){
12321             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12322         }
12323         
12324         if(this.removable && !this.editable && !this.tickable){
12325             var close = this.closeTriggerEl();
12326             
12327             if(close){
12328                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12329                 close.on('click', this.removeBtnClick, this, close);
12330             }
12331         }
12332         
12333         //this.trigger.addClassOnOver('x-form-trigger-over');
12334         //this.trigger.addClassOnClick('x-form-trigger-click');
12335         
12336         //if(!this.width){
12337         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12338         //}
12339     },
12340     
12341     closeTriggerEl : function()
12342     {
12343         var close = this.el.select('.roo-combo-removable-btn', true).first();
12344         return close ? close : false;
12345     },
12346     
12347     removeBtnClick : function(e, h, el)
12348     {
12349         e.preventDefault();
12350         
12351         if(this.fireEvent("remove", this) !== false){
12352             this.reset();
12353             this.fireEvent("afterremove", this)
12354         }
12355     },
12356     
12357     createList : function()
12358     {
12359         this.list = Roo.get(document.body).createChild({
12360             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12361             cls: 'typeahead typeahead-long dropdown-menu shadow',
12362             style: 'display:none'
12363         });
12364         
12365         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12366         
12367     },
12368
12369     // private
12370     initTrigger : function(){
12371        
12372     },
12373
12374     // private
12375     onDestroy : function(){
12376         if(this.trigger){
12377             this.trigger.removeAllListeners();
12378           //  this.trigger.remove();
12379         }
12380         //if(this.wrap){
12381         //    this.wrap.remove();
12382         //}
12383         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12384     },
12385
12386     // private
12387     onFocus : function(){
12388         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12389         /*
12390         if(!this.mimicing){
12391             this.wrap.addClass('x-trigger-wrap-focus');
12392             this.mimicing = true;
12393             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12394             if(this.monitorTab){
12395                 this.el.on("keydown", this.checkTab, this);
12396             }
12397         }
12398         */
12399     },
12400
12401     // private
12402     checkTab : function(e){
12403         if(e.getKey() == e.TAB){
12404             this.triggerBlur();
12405         }
12406     },
12407
12408     // private
12409     onBlur : function(){
12410         // do nothing
12411     },
12412
12413     // private
12414     mimicBlur : function(e, t){
12415         /*
12416         if(!this.wrap.contains(t) && this.validateBlur()){
12417             this.triggerBlur();
12418         }
12419         */
12420     },
12421
12422     // private
12423     triggerBlur : function(){
12424         this.mimicing = false;
12425         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12426         if(this.monitorTab){
12427             this.el.un("keydown", this.checkTab, this);
12428         }
12429         //this.wrap.removeClass('x-trigger-wrap-focus');
12430         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12431     },
12432
12433     // private
12434     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12435     validateBlur : function(e, t){
12436         return true;
12437     },
12438
12439     // private
12440     onDisable : function(){
12441         this.inputEl().dom.disabled = true;
12442         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12443         //if(this.wrap){
12444         //    this.wrap.addClass('x-item-disabled');
12445         //}
12446     },
12447
12448     // private
12449     onEnable : function(){
12450         this.inputEl().dom.disabled = false;
12451         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12452         //if(this.wrap){
12453         //    this.el.removeClass('x-item-disabled');
12454         //}
12455     },
12456
12457     // private
12458     onShow : function(){
12459         var ae = this.getActionEl();
12460         
12461         if(ae){
12462             ae.dom.style.display = '';
12463             ae.dom.style.visibility = 'visible';
12464         }
12465     },
12466
12467     // private
12468     
12469     onHide : function(){
12470         var ae = this.getActionEl();
12471         ae.dom.style.display = 'none';
12472     },
12473
12474     /**
12475      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12476      * by an implementing function.
12477      * @method
12478      * @param {EventObject} e
12479      */
12480     onTriggerClick : Roo.emptyFn
12481 });
12482  
12483 /*
12484 * Licence: LGPL
12485 */
12486
12487 /**
12488  * @class Roo.bootstrap.CardUploader
12489  * @extends Roo.bootstrap.Button
12490  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12491  * @cfg {Number} errorTimeout default 3000
12492  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12493  * @cfg {Array}  html The button text.
12494
12495  *
12496  * @constructor
12497  * Create a new CardUploader
12498  * @param {Object} config The config object
12499  */
12500
12501 Roo.bootstrap.CardUploader = function(config){
12502     
12503  
12504     
12505     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12506     
12507     
12508     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12509         return r.data.id
12510      });
12511     
12512      this.addEvents({
12513          // raw events
12514         /**
12515          * @event preview
12516          * When a image is clicked on - and needs to display a slideshow or similar..
12517          * @param {Roo.bootstrap.Card} this
12518          * @param {Object} The image information data 
12519          *
12520          */
12521         'preview' : true,
12522          /**
12523          * @event download
12524          * When a the download link is clicked
12525          * @param {Roo.bootstrap.Card} this
12526          * @param {Object} The image information data  contains 
12527          */
12528         'download' : true
12529         
12530     });
12531 };
12532  
12533 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12534     
12535      
12536     errorTimeout : 3000,
12537      
12538     images : false,
12539    
12540     fileCollection : false,
12541     allowBlank : true,
12542     
12543     getAutoCreate : function()
12544     {
12545         
12546         var cfg =  {
12547             cls :'form-group' ,
12548             cn : [
12549                
12550                 {
12551                     tag: 'label',
12552                    //cls : 'input-group-addon',
12553                     html : this.fieldLabel
12554
12555                 },
12556
12557                 {
12558                     tag: 'input',
12559                     type : 'hidden',
12560                     name : this.name,
12561                     value : this.value,
12562                     cls : 'd-none  form-control'
12563                 },
12564                 
12565                 {
12566                     tag: 'input',
12567                     multiple : 'multiple',
12568                     type : 'file',
12569                     cls : 'd-none  roo-card-upload-selector'
12570                 },
12571                 
12572                 {
12573                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12574                 },
12575                 {
12576                     cls : 'card-columns roo-card-uploader-container'
12577                 }
12578
12579             ]
12580         };
12581            
12582          
12583         return cfg;
12584     },
12585     
12586     getChildContainer : function() /// what children are added to.
12587     {
12588         return this.containerEl;
12589     },
12590    
12591     getButtonContainer : function() /// what children are added to.
12592     {
12593         return this.el.select(".roo-card-uploader-button-container").first();
12594     },
12595    
12596     initEvents : function()
12597     {
12598         
12599         Roo.bootstrap.Input.prototype.initEvents.call(this);
12600         
12601         var t = this;
12602         this.addxtype({
12603             xns: Roo.bootstrap,
12604
12605             xtype : 'Button',
12606             container_method : 'getButtonContainer' ,            
12607             html :  this.html, // fix changable?
12608             cls : 'w-100 ',
12609             listeners : {
12610                 'click' : function(btn, e) {
12611                     t.onClick(e);
12612                 }
12613             }
12614         });
12615         
12616         
12617         
12618         
12619         this.urlAPI = (window.createObjectURL && window) || 
12620                                 (window.URL && URL.revokeObjectURL && URL) || 
12621                                 (window.webkitURL && webkitURL);
12622                         
12623          
12624          
12625          
12626         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12627         
12628         this.selectorEl.on('change', this.onFileSelected, this);
12629         if (this.images) {
12630             var t = this;
12631             this.images.forEach(function(img) {
12632                 t.addCard(img)
12633             });
12634             this.images = false;
12635         }
12636         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12637          
12638        
12639     },
12640     
12641    
12642     onClick : function(e)
12643     {
12644         e.preventDefault();
12645          
12646         this.selectorEl.dom.click();
12647          
12648     },
12649     
12650     onFileSelected : function(e)
12651     {
12652         e.preventDefault();
12653         
12654         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12655             return;
12656         }
12657         
12658         Roo.each(this.selectorEl.dom.files, function(file){    
12659             this.addFile(file);
12660         }, this);
12661          
12662     },
12663     
12664       
12665     
12666       
12667     
12668     addFile : function(file)
12669     {
12670            
12671         if(typeof(file) === 'string'){
12672             throw "Add file by name?"; // should not happen
12673             return;
12674         }
12675         
12676         if(!file || !this.urlAPI){
12677             return;
12678         }
12679         
12680         // file;
12681         // file.type;
12682         
12683         var _this = this;
12684         
12685         
12686         var url = _this.urlAPI.createObjectURL( file);
12687            
12688         this.addCard({
12689             id : Roo.bootstrap.CardUploader.ID--,
12690             is_uploaded : false,
12691             src : url,
12692             srcfile : file,
12693             title : file.name,
12694             mimetype : file.type,
12695             preview : false,
12696             is_deleted : 0
12697         });
12698         
12699     },
12700     
12701     /**
12702      * addCard - add an Attachment to the uploader
12703      * @param data - the data about the image to upload
12704      *
12705      * {
12706           id : 123
12707           title : "Title of file",
12708           is_uploaded : false,
12709           src : "http://.....",
12710           srcfile : { the File upload object },
12711           mimetype : file.type,
12712           preview : false,
12713           is_deleted : 0
12714           .. any other data...
12715         }
12716      *
12717      * 
12718     */
12719     
12720     addCard : function (data)
12721     {
12722         // hidden input element?
12723         // if the file is not an image...
12724         //then we need to use something other that and header_image
12725         var t = this;
12726         //   remove.....
12727         var footer = [
12728             {
12729                 xns : Roo.bootstrap,
12730                 xtype : 'CardFooter',
12731                  items: [
12732                     {
12733                         xns : Roo.bootstrap,
12734                         xtype : 'Element',
12735                         cls : 'd-flex',
12736                         items : [
12737                             
12738                             {
12739                                 xns : Roo.bootstrap,
12740                                 xtype : 'Button',
12741                                 html : String.format("<small>{0}</small>", data.title),
12742                                 cls : 'col-10 text-left',
12743                                 size: 'sm',
12744                                 weight: 'link',
12745                                 fa : 'download',
12746                                 listeners : {
12747                                     click : function() {
12748                                      
12749                                         t.fireEvent( "download", t, data );
12750                                     }
12751                                 }
12752                             },
12753                           
12754                             {
12755                                 xns : Roo.bootstrap,
12756                                 xtype : 'Button',
12757                                 style: 'max-height: 28px; ',
12758                                 size : 'sm',
12759                                 weight: 'danger',
12760                                 cls : 'col-2',
12761                                 fa : 'times',
12762                                 listeners : {
12763                                     click : function() {
12764                                         t.removeCard(data.id)
12765                                     }
12766                                 }
12767                             }
12768                         ]
12769                     }
12770                     
12771                 ] 
12772             }
12773             
12774         ];
12775         
12776         var cn = this.addxtype(
12777             {
12778                  
12779                 xns : Roo.bootstrap,
12780                 xtype : 'Card',
12781                 closeable : true,
12782                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12783                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12784                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12785                 data : data,
12786                 html : false,
12787                  
12788                 items : footer,
12789                 initEvents : function() {
12790                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12791                     var card = this;
12792                     this.imgEl = this.el.select('.card-img-top').first();
12793                     if (this.imgEl) {
12794                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12795                         this.imgEl.set({ 'pointer' : 'cursor' });
12796                                   
12797                     }
12798                     this.getCardFooter().addClass('p-1');
12799                     
12800                   
12801                 }
12802                 
12803             }
12804         );
12805         // dont' really need ot update items.
12806         // this.items.push(cn);
12807         this.fileCollection.add(cn);
12808         
12809         if (!data.srcfile) {
12810             this.updateInput();
12811             return;
12812         }
12813             
12814         var _t = this;
12815         var reader = new FileReader();
12816         reader.addEventListener("load", function() {  
12817             data.srcdata =  reader.result;
12818             _t.updateInput();
12819         });
12820         reader.readAsDataURL(data.srcfile);
12821         
12822         
12823         
12824     },
12825     removeCard : function(id)
12826     {
12827         
12828         var card  = this.fileCollection.get(id);
12829         card.data.is_deleted = 1;
12830         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12831         //this.fileCollection.remove(card);
12832         //this.items = this.items.filter(function(e) { return e != card });
12833         // dont' really need ot update items.
12834         card.el.dom.parentNode.removeChild(card.el.dom);
12835         this.updateInput();
12836
12837         
12838     },
12839     reset: function()
12840     {
12841         this.fileCollection.each(function(card) {
12842             if (card.el.dom && card.el.dom.parentNode) {
12843                 card.el.dom.parentNode.removeChild(card.el.dom);
12844             }
12845         });
12846         this.fileCollection.clear();
12847         this.updateInput();
12848     },
12849     
12850     updateInput : function()
12851     {
12852          var data = [];
12853         this.fileCollection.each(function(e) {
12854             data.push(e.data);
12855             
12856         });
12857         this.inputEl().dom.value = JSON.stringify(data);
12858         
12859         
12860         
12861     }
12862     
12863     
12864 });
12865
12866
12867 Roo.bootstrap.CardUploader.ID = -1;/*
12868  * Based on:
12869  * Ext JS Library 1.1.1
12870  * Copyright(c) 2006-2007, Ext JS, LLC.
12871  *
12872  * Originally Released Under LGPL - original licence link has changed is not relivant.
12873  *
12874  * Fork - LGPL
12875  * <script type="text/javascript">
12876  */
12877
12878
12879 /**
12880  * @class Roo.data.SortTypes
12881  * @singleton
12882  * Defines the default sorting (casting?) comparison functions used when sorting data.
12883  */
12884 Roo.data.SortTypes = {
12885     /**
12886      * Default sort that does nothing
12887      * @param {Mixed} s The value being converted
12888      * @return {Mixed} The comparison value
12889      */
12890     none : function(s){
12891         return s;
12892     },
12893     
12894     /**
12895      * The regular expression used to strip tags
12896      * @type {RegExp}
12897      * @property
12898      */
12899     stripTagsRE : /<\/?[^>]+>/gi,
12900     
12901     /**
12902      * Strips all HTML tags to sort on text only
12903      * @param {Mixed} s The value being converted
12904      * @return {String} The comparison value
12905      */
12906     asText : function(s){
12907         return String(s).replace(this.stripTagsRE, "");
12908     },
12909     
12910     /**
12911      * Strips all HTML tags to sort on text only - Case insensitive
12912      * @param {Mixed} s The value being converted
12913      * @return {String} The comparison value
12914      */
12915     asUCText : function(s){
12916         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12917     },
12918     
12919     /**
12920      * Case insensitive string
12921      * @param {Mixed} s The value being converted
12922      * @return {String} The comparison value
12923      */
12924     asUCString : function(s) {
12925         return String(s).toUpperCase();
12926     },
12927     
12928     /**
12929      * Date sorting
12930      * @param {Mixed} s The value being converted
12931      * @return {Number} The comparison value
12932      */
12933     asDate : function(s) {
12934         if(!s){
12935             return 0;
12936         }
12937         if(s instanceof Date){
12938             return s.getTime();
12939         }
12940         return Date.parse(String(s));
12941     },
12942     
12943     /**
12944      * Float sorting
12945      * @param {Mixed} s The value being converted
12946      * @return {Float} The comparison value
12947      */
12948     asFloat : function(s) {
12949         var val = parseFloat(String(s).replace(/,/g, ""));
12950         if(isNaN(val)) {
12951             val = 0;
12952         }
12953         return val;
12954     },
12955     
12956     /**
12957      * Integer sorting
12958      * @param {Mixed} s The value being converted
12959      * @return {Number} The comparison value
12960      */
12961     asInt : function(s) {
12962         var val = parseInt(String(s).replace(/,/g, ""));
12963         if(isNaN(val)) {
12964             val = 0;
12965         }
12966         return val;
12967     }
12968 };/*
12969  * Based on:
12970  * Ext JS Library 1.1.1
12971  * Copyright(c) 2006-2007, Ext JS, LLC.
12972  *
12973  * Originally Released Under LGPL - original licence link has changed is not relivant.
12974  *
12975  * Fork - LGPL
12976  * <script type="text/javascript">
12977  */
12978
12979 /**
12980 * @class Roo.data.Record
12981  * Instances of this class encapsulate both record <em>definition</em> information, and record
12982  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12983  * to access Records cached in an {@link Roo.data.Store} object.<br>
12984  * <p>
12985  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12986  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12987  * objects.<br>
12988  * <p>
12989  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12990  * @constructor
12991  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12992  * {@link #create}. The parameters are the same.
12993  * @param {Array} data An associative Array of data values keyed by the field name.
12994  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12995  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12996  * not specified an integer id is generated.
12997  */
12998 Roo.data.Record = function(data, id){
12999     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13000     this.data = data;
13001 };
13002
13003 /**
13004  * Generate a constructor for a specific record layout.
13005  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13006  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13007  * Each field definition object may contain the following properties: <ul>
13008  * <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,
13009  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13010  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13011  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13012  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13013  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13014  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13015  * this may be omitted.</p></li>
13016  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13017  * <ul><li>auto (Default, implies no conversion)</li>
13018  * <li>string</li>
13019  * <li>int</li>
13020  * <li>float</li>
13021  * <li>boolean</li>
13022  * <li>date</li></ul></p></li>
13023  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13024  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13025  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13026  * by the Reader into an object that will be stored in the Record. It is passed the
13027  * following parameters:<ul>
13028  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13029  * </ul></p></li>
13030  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13031  * </ul>
13032  * <br>usage:<br><pre><code>
13033 var TopicRecord = Roo.data.Record.create(
13034     {name: 'title', mapping: 'topic_title'},
13035     {name: 'author', mapping: 'username'},
13036     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13037     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13038     {name: 'lastPoster', mapping: 'user2'},
13039     {name: 'excerpt', mapping: 'post_text'}
13040 );
13041
13042 var myNewRecord = new TopicRecord({
13043     title: 'Do my job please',
13044     author: 'noobie',
13045     totalPosts: 1,
13046     lastPost: new Date(),
13047     lastPoster: 'Animal',
13048     excerpt: 'No way dude!'
13049 });
13050 myStore.add(myNewRecord);
13051 </code></pre>
13052  * @method create
13053  * @static
13054  */
13055 Roo.data.Record.create = function(o){
13056     var f = function(){
13057         f.superclass.constructor.apply(this, arguments);
13058     };
13059     Roo.extend(f, Roo.data.Record);
13060     var p = f.prototype;
13061     p.fields = new Roo.util.MixedCollection(false, function(field){
13062         return field.name;
13063     });
13064     for(var i = 0, len = o.length; i < len; i++){
13065         p.fields.add(new Roo.data.Field(o[i]));
13066     }
13067     f.getField = function(name){
13068         return p.fields.get(name);  
13069     };
13070     return f;
13071 };
13072
13073 Roo.data.Record.AUTO_ID = 1000;
13074 Roo.data.Record.EDIT = 'edit';
13075 Roo.data.Record.REJECT = 'reject';
13076 Roo.data.Record.COMMIT = 'commit';
13077
13078 Roo.data.Record.prototype = {
13079     /**
13080      * Readonly flag - true if this record has been modified.
13081      * @type Boolean
13082      */
13083     dirty : false,
13084     editing : false,
13085     error: null,
13086     modified: null,
13087
13088     // private
13089     join : function(store){
13090         this.store = store;
13091     },
13092
13093     /**
13094      * Set the named field to the specified value.
13095      * @param {String} name The name of the field to set.
13096      * @param {Object} value The value to set the field to.
13097      */
13098     set : function(name, value){
13099         if(this.data[name] == value){
13100             return;
13101         }
13102         this.dirty = true;
13103         if(!this.modified){
13104             this.modified = {};
13105         }
13106         if(typeof this.modified[name] == 'undefined'){
13107             this.modified[name] = this.data[name];
13108         }
13109         this.data[name] = value;
13110         if(!this.editing && this.store){
13111             this.store.afterEdit(this);
13112         }       
13113     },
13114
13115     /**
13116      * Get the value of the named field.
13117      * @param {String} name The name of the field to get the value of.
13118      * @return {Object} The value of the field.
13119      */
13120     get : function(name){
13121         return this.data[name]; 
13122     },
13123
13124     // private
13125     beginEdit : function(){
13126         this.editing = true;
13127         this.modified = {}; 
13128     },
13129
13130     // private
13131     cancelEdit : function(){
13132         this.editing = false;
13133         delete this.modified;
13134     },
13135
13136     // private
13137     endEdit : function(){
13138         this.editing = false;
13139         if(this.dirty && this.store){
13140             this.store.afterEdit(this);
13141         }
13142     },
13143
13144     /**
13145      * Usually called by the {@link Roo.data.Store} which owns the Record.
13146      * Rejects all changes made to the Record since either creation, or the last commit operation.
13147      * Modified fields are reverted to their original values.
13148      * <p>
13149      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13150      * of reject operations.
13151      */
13152     reject : function(){
13153         var m = this.modified;
13154         for(var n in m){
13155             if(typeof m[n] != "function"){
13156                 this.data[n] = m[n];
13157             }
13158         }
13159         this.dirty = false;
13160         delete this.modified;
13161         this.editing = false;
13162         if(this.store){
13163             this.store.afterReject(this);
13164         }
13165     },
13166
13167     /**
13168      * Usually called by the {@link Roo.data.Store} which owns the Record.
13169      * Commits all changes made to the Record since either creation, or the last commit operation.
13170      * <p>
13171      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13172      * of commit operations.
13173      */
13174     commit : function(){
13175         this.dirty = false;
13176         delete this.modified;
13177         this.editing = false;
13178         if(this.store){
13179             this.store.afterCommit(this);
13180         }
13181     },
13182
13183     // private
13184     hasError : function(){
13185         return this.error != null;
13186     },
13187
13188     // private
13189     clearError : function(){
13190         this.error = null;
13191     },
13192
13193     /**
13194      * Creates a copy of this record.
13195      * @param {String} id (optional) A new record id if you don't want to use this record's id
13196      * @return {Record}
13197      */
13198     copy : function(newId) {
13199         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13200     }
13201 };/*
13202  * Based on:
13203  * Ext JS Library 1.1.1
13204  * Copyright(c) 2006-2007, Ext JS, LLC.
13205  *
13206  * Originally Released Under LGPL - original licence link has changed is not relivant.
13207  *
13208  * Fork - LGPL
13209  * <script type="text/javascript">
13210  */
13211
13212
13213
13214 /**
13215  * @class Roo.data.Store
13216  * @extends Roo.util.Observable
13217  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13218  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13219  * <p>
13220  * 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
13221  * has no knowledge of the format of the data returned by the Proxy.<br>
13222  * <p>
13223  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13224  * instances from the data object. These records are cached and made available through accessor functions.
13225  * @constructor
13226  * Creates a new Store.
13227  * @param {Object} config A config object containing the objects needed for the Store to access data,
13228  * and read the data into Records.
13229  */
13230 Roo.data.Store = function(config){
13231     this.data = new Roo.util.MixedCollection(false);
13232     this.data.getKey = function(o){
13233         return o.id;
13234     };
13235     this.baseParams = {};
13236     // private
13237     this.paramNames = {
13238         "start" : "start",
13239         "limit" : "limit",
13240         "sort" : "sort",
13241         "dir" : "dir",
13242         "multisort" : "_multisort"
13243     };
13244
13245     if(config && config.data){
13246         this.inlineData = config.data;
13247         delete config.data;
13248     }
13249
13250     Roo.apply(this, config);
13251     
13252     if(this.reader){ // reader passed
13253         this.reader = Roo.factory(this.reader, Roo.data);
13254         this.reader.xmodule = this.xmodule || false;
13255         if(!this.recordType){
13256             this.recordType = this.reader.recordType;
13257         }
13258         if(this.reader.onMetaChange){
13259             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13260         }
13261     }
13262
13263     if(this.recordType){
13264         this.fields = this.recordType.prototype.fields;
13265     }
13266     this.modified = [];
13267
13268     this.addEvents({
13269         /**
13270          * @event datachanged
13271          * Fires when the data cache has changed, and a widget which is using this Store
13272          * as a Record cache should refresh its view.
13273          * @param {Store} this
13274          */
13275         datachanged : true,
13276         /**
13277          * @event metachange
13278          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13279          * @param {Store} this
13280          * @param {Object} meta The JSON metadata
13281          */
13282         metachange : true,
13283         /**
13284          * @event add
13285          * Fires when Records have been added to the Store
13286          * @param {Store} this
13287          * @param {Roo.data.Record[]} records The array of Records added
13288          * @param {Number} index The index at which the record(s) were added
13289          */
13290         add : true,
13291         /**
13292          * @event remove
13293          * Fires when a Record has been removed from the Store
13294          * @param {Store} this
13295          * @param {Roo.data.Record} record The Record that was removed
13296          * @param {Number} index The index at which the record was removed
13297          */
13298         remove : true,
13299         /**
13300          * @event update
13301          * Fires when a Record has been updated
13302          * @param {Store} this
13303          * @param {Roo.data.Record} record The Record that was updated
13304          * @param {String} operation The update operation being performed.  Value may be one of:
13305          * <pre><code>
13306  Roo.data.Record.EDIT
13307  Roo.data.Record.REJECT
13308  Roo.data.Record.COMMIT
13309          * </code></pre>
13310          */
13311         update : true,
13312         /**
13313          * @event clear
13314          * Fires when the data cache has been cleared.
13315          * @param {Store} this
13316          */
13317         clear : true,
13318         /**
13319          * @event beforeload
13320          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13321          * the load action will be canceled.
13322          * @param {Store} this
13323          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13324          */
13325         beforeload : true,
13326         /**
13327          * @event beforeloadadd
13328          * Fires after a new set of Records has been loaded.
13329          * @param {Store} this
13330          * @param {Roo.data.Record[]} records The Records that were loaded
13331          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13332          */
13333         beforeloadadd : true,
13334         /**
13335          * @event load
13336          * Fires after a new set of Records has been loaded, before they are added to the store.
13337          * @param {Store} this
13338          * @param {Roo.data.Record[]} records The Records that were loaded
13339          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13340          * @params {Object} return from reader
13341          */
13342         load : true,
13343         /**
13344          * @event loadexception
13345          * Fires if an exception occurs in the Proxy during loading.
13346          * Called with the signature of the Proxy's "loadexception" event.
13347          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13348          * 
13349          * @param {Proxy} 
13350          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13351          * @param {Object} load options 
13352          * @param {Object} jsonData from your request (normally this contains the Exception)
13353          */
13354         loadexception : true
13355     });
13356     
13357     if(this.proxy){
13358         this.proxy = Roo.factory(this.proxy, Roo.data);
13359         this.proxy.xmodule = this.xmodule || false;
13360         this.relayEvents(this.proxy,  ["loadexception"]);
13361     }
13362     this.sortToggle = {};
13363     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13364
13365     Roo.data.Store.superclass.constructor.call(this);
13366
13367     if(this.inlineData){
13368         this.loadData(this.inlineData);
13369         delete this.inlineData;
13370     }
13371 };
13372
13373 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13374      /**
13375     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13376     * without a remote query - used by combo/forms at present.
13377     */
13378     
13379     /**
13380     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13381     */
13382     /**
13383     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13384     */
13385     /**
13386     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13387     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13388     */
13389     /**
13390     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13391     * on any HTTP request
13392     */
13393     /**
13394     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13395     */
13396     /**
13397     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13398     */
13399     multiSort: false,
13400     /**
13401     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13402     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13403     */
13404     remoteSort : false,
13405
13406     /**
13407     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13408      * loaded or when a record is removed. (defaults to false).
13409     */
13410     pruneModifiedRecords : false,
13411
13412     // private
13413     lastOptions : null,
13414
13415     /**
13416      * Add Records to the Store and fires the add event.
13417      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13418      */
13419     add : function(records){
13420         records = [].concat(records);
13421         for(var i = 0, len = records.length; i < len; i++){
13422             records[i].join(this);
13423         }
13424         var index = this.data.length;
13425         this.data.addAll(records);
13426         this.fireEvent("add", this, records, index);
13427     },
13428
13429     /**
13430      * Remove a Record from the Store and fires the remove event.
13431      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13432      */
13433     remove : function(record){
13434         var index = this.data.indexOf(record);
13435         this.data.removeAt(index);
13436  
13437         if(this.pruneModifiedRecords){
13438             this.modified.remove(record);
13439         }
13440         this.fireEvent("remove", this, record, index);
13441     },
13442
13443     /**
13444      * Remove all Records from the Store and fires the clear event.
13445      */
13446     removeAll : function(){
13447         this.data.clear();
13448         if(this.pruneModifiedRecords){
13449             this.modified = [];
13450         }
13451         this.fireEvent("clear", this);
13452     },
13453
13454     /**
13455      * Inserts Records to the Store at the given index and fires the add event.
13456      * @param {Number} index The start index at which to insert the passed Records.
13457      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13458      */
13459     insert : function(index, records){
13460         records = [].concat(records);
13461         for(var i = 0, len = records.length; i < len; i++){
13462             this.data.insert(index, records[i]);
13463             records[i].join(this);
13464         }
13465         this.fireEvent("add", this, records, index);
13466     },
13467
13468     /**
13469      * Get the index within the cache of the passed Record.
13470      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13471      * @return {Number} The index of the passed Record. Returns -1 if not found.
13472      */
13473     indexOf : function(record){
13474         return this.data.indexOf(record);
13475     },
13476
13477     /**
13478      * Get the index within the cache of the Record with the passed id.
13479      * @param {String} id The id of the Record to find.
13480      * @return {Number} The index of the Record. Returns -1 if not found.
13481      */
13482     indexOfId : function(id){
13483         return this.data.indexOfKey(id);
13484     },
13485
13486     /**
13487      * Get the Record with the specified id.
13488      * @param {String} id The id of the Record to find.
13489      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13490      */
13491     getById : function(id){
13492         return this.data.key(id);
13493     },
13494
13495     /**
13496      * Get the Record at the specified index.
13497      * @param {Number} index The index of the Record to find.
13498      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13499      */
13500     getAt : function(index){
13501         return this.data.itemAt(index);
13502     },
13503
13504     /**
13505      * Returns a range of Records between specified indices.
13506      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13507      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13508      * @return {Roo.data.Record[]} An array of Records
13509      */
13510     getRange : function(start, end){
13511         return this.data.getRange(start, end);
13512     },
13513
13514     // private
13515     storeOptions : function(o){
13516         o = Roo.apply({}, o);
13517         delete o.callback;
13518         delete o.scope;
13519         this.lastOptions = o;
13520     },
13521
13522     /**
13523      * Loads the Record cache from the configured Proxy using the configured Reader.
13524      * <p>
13525      * If using remote paging, then the first load call must specify the <em>start</em>
13526      * and <em>limit</em> properties in the options.params property to establish the initial
13527      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13528      * <p>
13529      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13530      * and this call will return before the new data has been loaded. Perform any post-processing
13531      * in a callback function, or in a "load" event handler.</strong>
13532      * <p>
13533      * @param {Object} options An object containing properties which control loading options:<ul>
13534      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13535      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13536      * passed the following arguments:<ul>
13537      * <li>r : Roo.data.Record[]</li>
13538      * <li>options: Options object from the load call</li>
13539      * <li>success: Boolean success indicator</li></ul></li>
13540      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13541      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13542      * </ul>
13543      */
13544     load : function(options){
13545         options = options || {};
13546         if(this.fireEvent("beforeload", this, options) !== false){
13547             this.storeOptions(options);
13548             var p = Roo.apply(options.params || {}, this.baseParams);
13549             // if meta was not loaded from remote source.. try requesting it.
13550             if (!this.reader.metaFromRemote) {
13551                 p._requestMeta = 1;
13552             }
13553             if(this.sortInfo && this.remoteSort){
13554                 var pn = this.paramNames;
13555                 p[pn["sort"]] = this.sortInfo.field;
13556                 p[pn["dir"]] = this.sortInfo.direction;
13557             }
13558             if (this.multiSort) {
13559                 var pn = this.paramNames;
13560                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13561             }
13562             
13563             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13564         }
13565     },
13566
13567     /**
13568      * Reloads the Record cache from the configured Proxy using the configured Reader and
13569      * the options from the last load operation performed.
13570      * @param {Object} options (optional) An object containing properties which may override the options
13571      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13572      * the most recently used options are reused).
13573      */
13574     reload : function(options){
13575         this.load(Roo.applyIf(options||{}, this.lastOptions));
13576     },
13577
13578     // private
13579     // Called as a callback by the Reader during a load operation.
13580     loadRecords : function(o, options, success){
13581         if(!o || success === false){
13582             if(success !== false){
13583                 this.fireEvent("load", this, [], options, o);
13584             }
13585             if(options.callback){
13586                 options.callback.call(options.scope || this, [], options, false);
13587             }
13588             return;
13589         }
13590         // if data returned failure - throw an exception.
13591         if (o.success === false) {
13592             // show a message if no listener is registered.
13593             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13594                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13595             }
13596             // loadmask wil be hooked into this..
13597             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13598             return;
13599         }
13600         var r = o.records, t = o.totalRecords || r.length;
13601         
13602         this.fireEvent("beforeloadadd", this, r, options, o);
13603         
13604         if(!options || options.add !== true){
13605             if(this.pruneModifiedRecords){
13606                 this.modified = [];
13607             }
13608             for(var i = 0, len = r.length; i < len; i++){
13609                 r[i].join(this);
13610             }
13611             if(this.snapshot){
13612                 this.data = this.snapshot;
13613                 delete this.snapshot;
13614             }
13615             this.data.clear();
13616             this.data.addAll(r);
13617             this.totalLength = t;
13618             this.applySort();
13619             this.fireEvent("datachanged", this);
13620         }else{
13621             this.totalLength = Math.max(t, this.data.length+r.length);
13622             this.add(r);
13623         }
13624         
13625         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13626                 
13627             var e = new Roo.data.Record({});
13628
13629             e.set(this.parent.displayField, this.parent.emptyTitle);
13630             e.set(this.parent.valueField, '');
13631
13632             this.insert(0, e);
13633         }
13634             
13635         this.fireEvent("load", this, r, options, o);
13636         if(options.callback){
13637             options.callback.call(options.scope || this, r, options, true);
13638         }
13639     },
13640
13641
13642     /**
13643      * Loads data from a passed data block. A Reader which understands the format of the data
13644      * must have been configured in the constructor.
13645      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13646      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13647      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13648      */
13649     loadData : function(o, append){
13650         var r = this.reader.readRecords(o);
13651         this.loadRecords(r, {add: append}, true);
13652     },
13653     
13654      /**
13655      * using 'cn' the nested child reader read the child array into it's child stores.
13656      * @param {Object} rec The record with a 'children array
13657      */
13658     loadDataFromChildren : function(rec)
13659     {
13660         this.loadData(this.reader.toLoadData(rec));
13661     },
13662     
13663
13664     /**
13665      * Gets the number of cached records.
13666      * <p>
13667      * <em>If using paging, this may not be the total size of the dataset. If the data object
13668      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13669      * the data set size</em>
13670      */
13671     getCount : function(){
13672         return this.data.length || 0;
13673     },
13674
13675     /**
13676      * Gets the total number of records in the dataset as returned by the server.
13677      * <p>
13678      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13679      * the dataset size</em>
13680      */
13681     getTotalCount : function(){
13682         return this.totalLength || 0;
13683     },
13684
13685     /**
13686      * Returns the sort state of the Store as an object with two properties:
13687      * <pre><code>
13688  field {String} The name of the field by which the Records are sorted
13689  direction {String} The sort order, "ASC" or "DESC"
13690      * </code></pre>
13691      */
13692     getSortState : function(){
13693         return this.sortInfo;
13694     },
13695
13696     // private
13697     applySort : function(){
13698         if(this.sortInfo && !this.remoteSort){
13699             var s = this.sortInfo, f = s.field;
13700             var st = this.fields.get(f).sortType;
13701             var fn = function(r1, r2){
13702                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13703                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13704             };
13705             this.data.sort(s.direction, fn);
13706             if(this.snapshot && this.snapshot != this.data){
13707                 this.snapshot.sort(s.direction, fn);
13708             }
13709         }
13710     },
13711
13712     /**
13713      * Sets the default sort column and order to be used by the next load operation.
13714      * @param {String} fieldName The name of the field to sort by.
13715      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13716      */
13717     setDefaultSort : function(field, dir){
13718         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13719     },
13720
13721     /**
13722      * Sort the Records.
13723      * If remote sorting is used, the sort is performed on the server, and the cache is
13724      * reloaded. If local sorting is used, the cache is sorted internally.
13725      * @param {String} fieldName The name of the field to sort by.
13726      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13727      */
13728     sort : function(fieldName, dir){
13729         var f = this.fields.get(fieldName);
13730         if(!dir){
13731             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13732             
13733             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13734                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13735             }else{
13736                 dir = f.sortDir;
13737             }
13738         }
13739         this.sortToggle[f.name] = dir;
13740         this.sortInfo = {field: f.name, direction: dir};
13741         if(!this.remoteSort){
13742             this.applySort();
13743             this.fireEvent("datachanged", this);
13744         }else{
13745             this.load(this.lastOptions);
13746         }
13747     },
13748
13749     /**
13750      * Calls the specified function for each of the Records in the cache.
13751      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13752      * Returning <em>false</em> aborts and exits the iteration.
13753      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13754      */
13755     each : function(fn, scope){
13756         this.data.each(fn, scope);
13757     },
13758
13759     /**
13760      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13761      * (e.g., during paging).
13762      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13763      */
13764     getModifiedRecords : function(){
13765         return this.modified;
13766     },
13767
13768     // private
13769     createFilterFn : function(property, value, anyMatch){
13770         if(!value.exec){ // not a regex
13771             value = String(value);
13772             if(value.length == 0){
13773                 return false;
13774             }
13775             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13776         }
13777         return function(r){
13778             return value.test(r.data[property]);
13779         };
13780     },
13781
13782     /**
13783      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13784      * @param {String} property A field on your records
13785      * @param {Number} start The record index to start at (defaults to 0)
13786      * @param {Number} end The last record index to include (defaults to length - 1)
13787      * @return {Number} The sum
13788      */
13789     sum : function(property, start, end){
13790         var rs = this.data.items, v = 0;
13791         start = start || 0;
13792         end = (end || end === 0) ? end : rs.length-1;
13793
13794         for(var i = start; i <= end; i++){
13795             v += (rs[i].data[property] || 0);
13796         }
13797         return v;
13798     },
13799
13800     /**
13801      * Filter the records by a specified property.
13802      * @param {String} field A field on your records
13803      * @param {String/RegExp} value Either a string that the field
13804      * should start with or a RegExp to test against the field
13805      * @param {Boolean} anyMatch True to match any part not just the beginning
13806      */
13807     filter : function(property, value, anyMatch){
13808         var fn = this.createFilterFn(property, value, anyMatch);
13809         return fn ? this.filterBy(fn) : this.clearFilter();
13810     },
13811
13812     /**
13813      * Filter by a function. The specified function will be called with each
13814      * record in this data source. If the function returns true the record is included,
13815      * otherwise it is filtered.
13816      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13817      * @param {Object} scope (optional) The scope of the function (defaults to this)
13818      */
13819     filterBy : function(fn, scope){
13820         this.snapshot = this.snapshot || this.data;
13821         this.data = this.queryBy(fn, scope||this);
13822         this.fireEvent("datachanged", this);
13823     },
13824
13825     /**
13826      * Query the records by a specified property.
13827      * @param {String} field A field on your records
13828      * @param {String/RegExp} value Either a string that the field
13829      * should start with or a RegExp to test against the field
13830      * @param {Boolean} anyMatch True to match any part not just the beginning
13831      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13832      */
13833     query : function(property, value, anyMatch){
13834         var fn = this.createFilterFn(property, value, anyMatch);
13835         return fn ? this.queryBy(fn) : this.data.clone();
13836     },
13837
13838     /**
13839      * Query by a function. The specified function will be called with each
13840      * record in this data source. If the function returns true the record is included
13841      * in the results.
13842      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13843      * @param {Object} scope (optional) The scope of the function (defaults to this)
13844       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13845      **/
13846     queryBy : function(fn, scope){
13847         var data = this.snapshot || this.data;
13848         return data.filterBy(fn, scope||this);
13849     },
13850
13851     /**
13852      * Collects unique values for a particular dataIndex from this store.
13853      * @param {String} dataIndex The property to collect
13854      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13855      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13856      * @return {Array} An array of the unique values
13857      **/
13858     collect : function(dataIndex, allowNull, bypassFilter){
13859         var d = (bypassFilter === true && this.snapshot) ?
13860                 this.snapshot.items : this.data.items;
13861         var v, sv, r = [], l = {};
13862         for(var i = 0, len = d.length; i < len; i++){
13863             v = d[i].data[dataIndex];
13864             sv = String(v);
13865             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13866                 l[sv] = true;
13867                 r[r.length] = v;
13868             }
13869         }
13870         return r;
13871     },
13872
13873     /**
13874      * Revert to a view of the Record cache with no filtering applied.
13875      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13876      */
13877     clearFilter : function(suppressEvent){
13878         if(this.snapshot && this.snapshot != this.data){
13879             this.data = this.snapshot;
13880             delete this.snapshot;
13881             if(suppressEvent !== true){
13882                 this.fireEvent("datachanged", this);
13883             }
13884         }
13885     },
13886
13887     // private
13888     afterEdit : function(record){
13889         if(this.modified.indexOf(record) == -1){
13890             this.modified.push(record);
13891         }
13892         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13893     },
13894     
13895     // private
13896     afterReject : function(record){
13897         this.modified.remove(record);
13898         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13899     },
13900
13901     // private
13902     afterCommit : function(record){
13903         this.modified.remove(record);
13904         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13905     },
13906
13907     /**
13908      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13909      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13910      */
13911     commitChanges : function(){
13912         var m = this.modified.slice(0);
13913         this.modified = [];
13914         for(var i = 0, len = m.length; i < len; i++){
13915             m[i].commit();
13916         }
13917     },
13918
13919     /**
13920      * Cancel outstanding changes on all changed records.
13921      */
13922     rejectChanges : function(){
13923         var m = this.modified.slice(0);
13924         this.modified = [];
13925         for(var i = 0, len = m.length; i < len; i++){
13926             m[i].reject();
13927         }
13928     },
13929
13930     onMetaChange : function(meta, rtype, o){
13931         this.recordType = rtype;
13932         this.fields = rtype.prototype.fields;
13933         delete this.snapshot;
13934         this.sortInfo = meta.sortInfo || this.sortInfo;
13935         this.modified = [];
13936         this.fireEvent('metachange', this, this.reader.meta);
13937     },
13938     
13939     moveIndex : function(data, type)
13940     {
13941         var index = this.indexOf(data);
13942         
13943         var newIndex = index + type;
13944         
13945         this.remove(data);
13946         
13947         this.insert(newIndex, data);
13948         
13949     }
13950 });/*
13951  * Based on:
13952  * Ext JS Library 1.1.1
13953  * Copyright(c) 2006-2007, Ext JS, LLC.
13954  *
13955  * Originally Released Under LGPL - original licence link has changed is not relivant.
13956  *
13957  * Fork - LGPL
13958  * <script type="text/javascript">
13959  */
13960
13961 /**
13962  * @class Roo.data.SimpleStore
13963  * @extends Roo.data.Store
13964  * Small helper class to make creating Stores from Array data easier.
13965  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13966  * @cfg {Array} fields An array of field definition objects, or field name strings.
13967  * @cfg {Object} an existing reader (eg. copied from another store)
13968  * @cfg {Array} data The multi-dimensional array of data
13969  * @constructor
13970  * @param {Object} config
13971  */
13972 Roo.data.SimpleStore = function(config)
13973 {
13974     Roo.data.SimpleStore.superclass.constructor.call(this, {
13975         isLocal : true,
13976         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13977                 id: config.id
13978             },
13979             Roo.data.Record.create(config.fields)
13980         ),
13981         proxy : new Roo.data.MemoryProxy(config.data)
13982     });
13983     this.load();
13984 };
13985 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13986  * Based on:
13987  * Ext JS Library 1.1.1
13988  * Copyright(c) 2006-2007, Ext JS, LLC.
13989  *
13990  * Originally Released Under LGPL - original licence link has changed is not relivant.
13991  *
13992  * Fork - LGPL
13993  * <script type="text/javascript">
13994  */
13995
13996 /**
13997 /**
13998  * @extends Roo.data.Store
13999  * @class Roo.data.JsonStore
14000  * Small helper class to make creating Stores for JSON data easier. <br/>
14001 <pre><code>
14002 var store = new Roo.data.JsonStore({
14003     url: 'get-images.php',
14004     root: 'images',
14005     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14006 });
14007 </code></pre>
14008  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14009  * JsonReader and HttpProxy (unless inline data is provided).</b>
14010  * @cfg {Array} fields An array of field definition objects, or field name strings.
14011  * @constructor
14012  * @param {Object} config
14013  */
14014 Roo.data.JsonStore = function(c){
14015     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14016         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14017         reader: new Roo.data.JsonReader(c, c.fields)
14018     }));
14019 };
14020 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14021  * Based on:
14022  * Ext JS Library 1.1.1
14023  * Copyright(c) 2006-2007, Ext JS, LLC.
14024  *
14025  * Originally Released Under LGPL - original licence link has changed is not relivant.
14026  *
14027  * Fork - LGPL
14028  * <script type="text/javascript">
14029  */
14030
14031  
14032 Roo.data.Field = function(config){
14033     if(typeof config == "string"){
14034         config = {name: config};
14035     }
14036     Roo.apply(this, config);
14037     
14038     if(!this.type){
14039         this.type = "auto";
14040     }
14041     
14042     var st = Roo.data.SortTypes;
14043     // named sortTypes are supported, here we look them up
14044     if(typeof this.sortType == "string"){
14045         this.sortType = st[this.sortType];
14046     }
14047     
14048     // set default sortType for strings and dates
14049     if(!this.sortType){
14050         switch(this.type){
14051             case "string":
14052                 this.sortType = st.asUCString;
14053                 break;
14054             case "date":
14055                 this.sortType = st.asDate;
14056                 break;
14057             default:
14058                 this.sortType = st.none;
14059         }
14060     }
14061
14062     // define once
14063     var stripRe = /[\$,%]/g;
14064
14065     // prebuilt conversion function for this field, instead of
14066     // switching every time we're reading a value
14067     if(!this.convert){
14068         var cv, dateFormat = this.dateFormat;
14069         switch(this.type){
14070             case "":
14071             case "auto":
14072             case undefined:
14073                 cv = function(v){ return v; };
14074                 break;
14075             case "string":
14076                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14077                 break;
14078             case "int":
14079                 cv = function(v){
14080                     return v !== undefined && v !== null && v !== '' ?
14081                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14082                     };
14083                 break;
14084             case "float":
14085                 cv = function(v){
14086                     return v !== undefined && v !== null && v !== '' ?
14087                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14088                     };
14089                 break;
14090             case "bool":
14091             case "boolean":
14092                 cv = function(v){ return v === true || v === "true" || v == 1; };
14093                 break;
14094             case "date":
14095                 cv = function(v){
14096                     if(!v){
14097                         return '';
14098                     }
14099                     if(v instanceof Date){
14100                         return v;
14101                     }
14102                     if(dateFormat){
14103                         if(dateFormat == "timestamp"){
14104                             return new Date(v*1000);
14105                         }
14106                         return Date.parseDate(v, dateFormat);
14107                     }
14108                     var parsed = Date.parse(v);
14109                     return parsed ? new Date(parsed) : null;
14110                 };
14111              break;
14112             
14113         }
14114         this.convert = cv;
14115     }
14116 };
14117
14118 Roo.data.Field.prototype = {
14119     dateFormat: null,
14120     defaultValue: "",
14121     mapping: null,
14122     sortType : null,
14123     sortDir : "ASC"
14124 };/*
14125  * Based on:
14126  * Ext JS Library 1.1.1
14127  * Copyright(c) 2006-2007, Ext JS, LLC.
14128  *
14129  * Originally Released Under LGPL - original licence link has changed is not relivant.
14130  *
14131  * Fork - LGPL
14132  * <script type="text/javascript">
14133  */
14134  
14135 // Base class for reading structured data from a data source.  This class is intended to be
14136 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14137
14138 /**
14139  * @class Roo.data.DataReader
14140  * Base class for reading structured data from a data source.  This class is intended to be
14141  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14142  */
14143
14144 Roo.data.DataReader = function(meta, recordType){
14145     
14146     this.meta = meta;
14147     
14148     this.recordType = recordType instanceof Array ? 
14149         Roo.data.Record.create(recordType) : recordType;
14150 };
14151
14152 Roo.data.DataReader.prototype = {
14153     
14154     
14155     readerType : 'Data',
14156      /**
14157      * Create an empty record
14158      * @param {Object} data (optional) - overlay some values
14159      * @return {Roo.data.Record} record created.
14160      */
14161     newRow :  function(d) {
14162         var da =  {};
14163         this.recordType.prototype.fields.each(function(c) {
14164             switch( c.type) {
14165                 case 'int' : da[c.name] = 0; break;
14166                 case 'date' : da[c.name] = new Date(); break;
14167                 case 'float' : da[c.name] = 0.0; break;
14168                 case 'boolean' : da[c.name] = false; break;
14169                 default : da[c.name] = ""; break;
14170             }
14171             
14172         });
14173         return new this.recordType(Roo.apply(da, d));
14174     }
14175     
14176     
14177 };/*
14178  * Based on:
14179  * Ext JS Library 1.1.1
14180  * Copyright(c) 2006-2007, Ext JS, LLC.
14181  *
14182  * Originally Released Under LGPL - original licence link has changed is not relivant.
14183  *
14184  * Fork - LGPL
14185  * <script type="text/javascript">
14186  */
14187
14188 /**
14189  * @class Roo.data.DataProxy
14190  * @extends Roo.data.Observable
14191  * This class is an abstract base class for implementations which provide retrieval of
14192  * unformatted data objects.<br>
14193  * <p>
14194  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14195  * (of the appropriate type which knows how to parse the data object) to provide a block of
14196  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14197  * <p>
14198  * Custom implementations must implement the load method as described in
14199  * {@link Roo.data.HttpProxy#load}.
14200  */
14201 Roo.data.DataProxy = function(){
14202     this.addEvents({
14203         /**
14204          * @event beforeload
14205          * Fires before a network request is made to retrieve a data object.
14206          * @param {Object} This DataProxy object.
14207          * @param {Object} params The params parameter to the load function.
14208          */
14209         beforeload : true,
14210         /**
14211          * @event load
14212          * Fires before the load method's callback is called.
14213          * @param {Object} This DataProxy object.
14214          * @param {Object} o The data object.
14215          * @param {Object} arg The callback argument object passed to the load function.
14216          */
14217         load : true,
14218         /**
14219          * @event loadexception
14220          * Fires if an Exception occurs during data retrieval.
14221          * @param {Object} This DataProxy object.
14222          * @param {Object} o The data object.
14223          * @param {Object} arg The callback argument object passed to the load function.
14224          * @param {Object} e The Exception.
14225          */
14226         loadexception : true
14227     });
14228     Roo.data.DataProxy.superclass.constructor.call(this);
14229 };
14230
14231 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14232
14233     /**
14234      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14235      */
14236 /*
14237  * Based on:
14238  * Ext JS Library 1.1.1
14239  * Copyright(c) 2006-2007, Ext JS, LLC.
14240  *
14241  * Originally Released Under LGPL - original licence link has changed is not relivant.
14242  *
14243  * Fork - LGPL
14244  * <script type="text/javascript">
14245  */
14246 /**
14247  * @class Roo.data.MemoryProxy
14248  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14249  * to the Reader when its load method is called.
14250  * @constructor
14251  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14252  */
14253 Roo.data.MemoryProxy = function(data){
14254     if (data.data) {
14255         data = data.data;
14256     }
14257     Roo.data.MemoryProxy.superclass.constructor.call(this);
14258     this.data = data;
14259 };
14260
14261 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14262     
14263     /**
14264      * Load data from the requested source (in this case an in-memory
14265      * data object passed to the constructor), read the data object into
14266      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14267      * process that block using the passed callback.
14268      * @param {Object} params This parameter is not used by the MemoryProxy class.
14269      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14270      * object into a block of Roo.data.Records.
14271      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14272      * The function must be passed <ul>
14273      * <li>The Record block object</li>
14274      * <li>The "arg" argument from the load function</li>
14275      * <li>A boolean success indicator</li>
14276      * </ul>
14277      * @param {Object} scope The scope in which to call the callback
14278      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14279      */
14280     load : function(params, reader, callback, scope, arg){
14281         params = params || {};
14282         var result;
14283         try {
14284             result = reader.readRecords(params.data ? params.data :this.data);
14285         }catch(e){
14286             this.fireEvent("loadexception", this, arg, null, e);
14287             callback.call(scope, null, arg, false);
14288             return;
14289         }
14290         callback.call(scope, result, arg, true);
14291     },
14292     
14293     // private
14294     update : function(params, records){
14295         
14296     }
14297 });/*
14298  * Based on:
14299  * Ext JS Library 1.1.1
14300  * Copyright(c) 2006-2007, Ext JS, LLC.
14301  *
14302  * Originally Released Under LGPL - original licence link has changed is not relivant.
14303  *
14304  * Fork - LGPL
14305  * <script type="text/javascript">
14306  */
14307 /**
14308  * @class Roo.data.HttpProxy
14309  * @extends Roo.data.DataProxy
14310  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14311  * configured to reference a certain URL.<br><br>
14312  * <p>
14313  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14314  * from which the running page was served.<br><br>
14315  * <p>
14316  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14317  * <p>
14318  * Be aware that to enable the browser to parse an XML document, the server must set
14319  * the Content-Type header in the HTTP response to "text/xml".
14320  * @constructor
14321  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14322  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14323  * will be used to make the request.
14324  */
14325 Roo.data.HttpProxy = function(conn){
14326     Roo.data.HttpProxy.superclass.constructor.call(this);
14327     // is conn a conn config or a real conn?
14328     this.conn = conn;
14329     this.useAjax = !conn || !conn.events;
14330   
14331 };
14332
14333 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14334     // thse are take from connection...
14335     
14336     /**
14337      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14338      */
14339     /**
14340      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14341      * extra parameters to each request made by this object. (defaults to undefined)
14342      */
14343     /**
14344      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14345      *  to each request made by this object. (defaults to undefined)
14346      */
14347     /**
14348      * @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)
14349      */
14350     /**
14351      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14352      */
14353      /**
14354      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14355      * @type Boolean
14356      */
14357   
14358
14359     /**
14360      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14361      * @type Boolean
14362      */
14363     /**
14364      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14365      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14366      * a finer-grained basis than the DataProxy events.
14367      */
14368     getConnection : function(){
14369         return this.useAjax ? Roo.Ajax : this.conn;
14370     },
14371
14372     /**
14373      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14374      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14375      * process that block using the passed callback.
14376      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14377      * for the request to the remote server.
14378      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14379      * object into a block of Roo.data.Records.
14380      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14381      * The function must be passed <ul>
14382      * <li>The Record block object</li>
14383      * <li>The "arg" argument from the load function</li>
14384      * <li>A boolean success indicator</li>
14385      * </ul>
14386      * @param {Object} scope The scope in which to call the callback
14387      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14388      */
14389     load : function(params, reader, callback, scope, arg){
14390         if(this.fireEvent("beforeload", this, params) !== false){
14391             var  o = {
14392                 params : params || {},
14393                 request: {
14394                     callback : callback,
14395                     scope : scope,
14396                     arg : arg
14397                 },
14398                 reader: reader,
14399                 callback : this.loadResponse,
14400                 scope: this
14401             };
14402             if(this.useAjax){
14403                 Roo.applyIf(o, this.conn);
14404                 if(this.activeRequest){
14405                     Roo.Ajax.abort(this.activeRequest);
14406                 }
14407                 this.activeRequest = Roo.Ajax.request(o);
14408             }else{
14409                 this.conn.request(o);
14410             }
14411         }else{
14412             callback.call(scope||this, null, arg, false);
14413         }
14414     },
14415
14416     // private
14417     loadResponse : function(o, success, response){
14418         delete this.activeRequest;
14419         if(!success){
14420             this.fireEvent("loadexception", this, o, response);
14421             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14422             return;
14423         }
14424         var result;
14425         try {
14426             result = o.reader.read(response);
14427         }catch(e){
14428             this.fireEvent("loadexception", this, o, response, e);
14429             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14430             return;
14431         }
14432         
14433         this.fireEvent("load", this, o, o.request.arg);
14434         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14435     },
14436
14437     // private
14438     update : function(dataSet){
14439
14440     },
14441
14442     // private
14443     updateResponse : function(dataSet){
14444
14445     }
14446 });/*
14447  * Based on:
14448  * Ext JS Library 1.1.1
14449  * Copyright(c) 2006-2007, Ext JS, LLC.
14450  *
14451  * Originally Released Under LGPL - original licence link has changed is not relivant.
14452  *
14453  * Fork - LGPL
14454  * <script type="text/javascript">
14455  */
14456
14457 /**
14458  * @class Roo.data.ScriptTagProxy
14459  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14460  * other than the originating domain of the running page.<br><br>
14461  * <p>
14462  * <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
14463  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14464  * <p>
14465  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14466  * source code that is used as the source inside a &lt;script> tag.<br><br>
14467  * <p>
14468  * In order for the browser to process the returned data, the server must wrap the data object
14469  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14470  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14471  * depending on whether the callback name was passed:
14472  * <p>
14473  * <pre><code>
14474 boolean scriptTag = false;
14475 String cb = request.getParameter("callback");
14476 if (cb != null) {
14477     scriptTag = true;
14478     response.setContentType("text/javascript");
14479 } else {
14480     response.setContentType("application/x-json");
14481 }
14482 Writer out = response.getWriter();
14483 if (scriptTag) {
14484     out.write(cb + "(");
14485 }
14486 out.print(dataBlock.toJsonString());
14487 if (scriptTag) {
14488     out.write(");");
14489 }
14490 </pre></code>
14491  *
14492  * @constructor
14493  * @param {Object} config A configuration object.
14494  */
14495 Roo.data.ScriptTagProxy = function(config){
14496     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14497     Roo.apply(this, config);
14498     this.head = document.getElementsByTagName("head")[0];
14499 };
14500
14501 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14502
14503 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14504     /**
14505      * @cfg {String} url The URL from which to request the data object.
14506      */
14507     /**
14508      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14509      */
14510     timeout : 30000,
14511     /**
14512      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14513      * the server the name of the callback function set up by the load call to process the returned data object.
14514      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14515      * javascript output which calls this named function passing the data object as its only parameter.
14516      */
14517     callbackParam : "callback",
14518     /**
14519      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14520      * name to the request.
14521      */
14522     nocache : true,
14523
14524     /**
14525      * Load data from the configured URL, read the data object into
14526      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14527      * process that block using the passed callback.
14528      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14529      * for the request to the remote server.
14530      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14531      * object into a block of Roo.data.Records.
14532      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14533      * The function must be passed <ul>
14534      * <li>The Record block object</li>
14535      * <li>The "arg" argument from the load function</li>
14536      * <li>A boolean success indicator</li>
14537      * </ul>
14538      * @param {Object} scope The scope in which to call the callback
14539      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14540      */
14541     load : function(params, reader, callback, scope, arg){
14542         if(this.fireEvent("beforeload", this, params) !== false){
14543
14544             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14545
14546             var url = this.url;
14547             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14548             if(this.nocache){
14549                 url += "&_dc=" + (new Date().getTime());
14550             }
14551             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14552             var trans = {
14553                 id : transId,
14554                 cb : "stcCallback"+transId,
14555                 scriptId : "stcScript"+transId,
14556                 params : params,
14557                 arg : arg,
14558                 url : url,
14559                 callback : callback,
14560                 scope : scope,
14561                 reader : reader
14562             };
14563             var conn = this;
14564
14565             window[trans.cb] = function(o){
14566                 conn.handleResponse(o, trans);
14567             };
14568
14569             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14570
14571             if(this.autoAbort !== false){
14572                 this.abort();
14573             }
14574
14575             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14576
14577             var script = document.createElement("script");
14578             script.setAttribute("src", url);
14579             script.setAttribute("type", "text/javascript");
14580             script.setAttribute("id", trans.scriptId);
14581             this.head.appendChild(script);
14582
14583             this.trans = trans;
14584         }else{
14585             callback.call(scope||this, null, arg, false);
14586         }
14587     },
14588
14589     // private
14590     isLoading : function(){
14591         return this.trans ? true : false;
14592     },
14593
14594     /**
14595      * Abort the current server request.
14596      */
14597     abort : function(){
14598         if(this.isLoading()){
14599             this.destroyTrans(this.trans);
14600         }
14601     },
14602
14603     // private
14604     destroyTrans : function(trans, isLoaded){
14605         this.head.removeChild(document.getElementById(trans.scriptId));
14606         clearTimeout(trans.timeoutId);
14607         if(isLoaded){
14608             window[trans.cb] = undefined;
14609             try{
14610                 delete window[trans.cb];
14611             }catch(e){}
14612         }else{
14613             // if hasn't been loaded, wait for load to remove it to prevent script error
14614             window[trans.cb] = function(){
14615                 window[trans.cb] = undefined;
14616                 try{
14617                     delete window[trans.cb];
14618                 }catch(e){}
14619             };
14620         }
14621     },
14622
14623     // private
14624     handleResponse : function(o, trans){
14625         this.trans = false;
14626         this.destroyTrans(trans, true);
14627         var result;
14628         try {
14629             result = trans.reader.readRecords(o);
14630         }catch(e){
14631             this.fireEvent("loadexception", this, o, trans.arg, e);
14632             trans.callback.call(trans.scope||window, null, trans.arg, false);
14633             return;
14634         }
14635         this.fireEvent("load", this, o, trans.arg);
14636         trans.callback.call(trans.scope||window, result, trans.arg, true);
14637     },
14638
14639     // private
14640     handleFailure : function(trans){
14641         this.trans = false;
14642         this.destroyTrans(trans, false);
14643         this.fireEvent("loadexception", this, null, trans.arg);
14644         trans.callback.call(trans.scope||window, null, trans.arg, false);
14645     }
14646 });/*
14647  * Based on:
14648  * Ext JS Library 1.1.1
14649  * Copyright(c) 2006-2007, Ext JS, LLC.
14650  *
14651  * Originally Released Under LGPL - original licence link has changed is not relivant.
14652  *
14653  * Fork - LGPL
14654  * <script type="text/javascript">
14655  */
14656
14657 /**
14658  * @class Roo.data.JsonReader
14659  * @extends Roo.data.DataReader
14660  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14661  * based on mappings in a provided Roo.data.Record constructor.
14662  * 
14663  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14664  * in the reply previously. 
14665  * 
14666  * <p>
14667  * Example code:
14668  * <pre><code>
14669 var RecordDef = Roo.data.Record.create([
14670     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14671     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14672 ]);
14673 var myReader = new Roo.data.JsonReader({
14674     totalProperty: "results",    // The property which contains the total dataset size (optional)
14675     root: "rows",                // The property which contains an Array of row objects
14676     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14677 }, RecordDef);
14678 </code></pre>
14679  * <p>
14680  * This would consume a JSON file like this:
14681  * <pre><code>
14682 { 'results': 2, 'rows': [
14683     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14684     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14685 }
14686 </code></pre>
14687  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14688  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14689  * paged from the remote server.
14690  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14691  * @cfg {String} root name of the property which contains the Array of row objects.
14692  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14693  * @cfg {Array} fields Array of field definition objects
14694  * @constructor
14695  * Create a new JsonReader
14696  * @param {Object} meta Metadata configuration options
14697  * @param {Object} recordType Either an Array of field definition objects,
14698  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14699  */
14700 Roo.data.JsonReader = function(meta, recordType){
14701     
14702     meta = meta || {};
14703     // set some defaults:
14704     Roo.applyIf(meta, {
14705         totalProperty: 'total',
14706         successProperty : 'success',
14707         root : 'data',
14708         id : 'id'
14709     });
14710     
14711     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14712 };
14713 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14714     
14715     readerType : 'Json',
14716     
14717     /**
14718      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14719      * Used by Store query builder to append _requestMeta to params.
14720      * 
14721      */
14722     metaFromRemote : false,
14723     /**
14724      * This method is only used by a DataProxy which has retrieved data from a remote server.
14725      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14726      * @return {Object} data A data block which is used by an Roo.data.Store object as
14727      * a cache of Roo.data.Records.
14728      */
14729     read : function(response){
14730         var json = response.responseText;
14731        
14732         var o = /* eval:var:o */ eval("("+json+")");
14733         if(!o) {
14734             throw {message: "JsonReader.read: Json object not found"};
14735         }
14736         
14737         if(o.metaData){
14738             
14739             delete this.ef;
14740             this.metaFromRemote = true;
14741             this.meta = o.metaData;
14742             this.recordType = Roo.data.Record.create(o.metaData.fields);
14743             this.onMetaChange(this.meta, this.recordType, o);
14744         }
14745         return this.readRecords(o);
14746     },
14747
14748     // private function a store will implement
14749     onMetaChange : function(meta, recordType, o){
14750
14751     },
14752
14753     /**
14754          * @ignore
14755          */
14756     simpleAccess: function(obj, subsc) {
14757         return obj[subsc];
14758     },
14759
14760         /**
14761          * @ignore
14762          */
14763     getJsonAccessor: function(){
14764         var re = /[\[\.]/;
14765         return function(expr) {
14766             try {
14767                 return(re.test(expr))
14768                     ? new Function("obj", "return obj." + expr)
14769                     : function(obj){
14770                         return obj[expr];
14771                     };
14772             } catch(e){}
14773             return Roo.emptyFn;
14774         };
14775     }(),
14776
14777     /**
14778      * Create a data block containing Roo.data.Records from an XML document.
14779      * @param {Object} o An object which contains an Array of row objects in the property specified
14780      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14781      * which contains the total size of the dataset.
14782      * @return {Object} data A data block which is used by an Roo.data.Store object as
14783      * a cache of Roo.data.Records.
14784      */
14785     readRecords : function(o){
14786         /**
14787          * After any data loads, the raw JSON data is available for further custom processing.
14788          * @type Object
14789          */
14790         this.o = o;
14791         var s = this.meta, Record = this.recordType,
14792             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14793
14794 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14795         if (!this.ef) {
14796             if(s.totalProperty) {
14797                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14798                 }
14799                 if(s.successProperty) {
14800                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14801                 }
14802                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14803                 if (s.id) {
14804                         var g = this.getJsonAccessor(s.id);
14805                         this.getId = function(rec) {
14806                                 var r = g(rec);  
14807                                 return (r === undefined || r === "") ? null : r;
14808                         };
14809                 } else {
14810                         this.getId = function(){return null;};
14811                 }
14812             this.ef = [];
14813             for(var jj = 0; jj < fl; jj++){
14814                 f = fi[jj];
14815                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14816                 this.ef[jj] = this.getJsonAccessor(map);
14817             }
14818         }
14819
14820         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14821         if(s.totalProperty){
14822             var vt = parseInt(this.getTotal(o), 10);
14823             if(!isNaN(vt)){
14824                 totalRecords = vt;
14825             }
14826         }
14827         if(s.successProperty){
14828             var vs = this.getSuccess(o);
14829             if(vs === false || vs === 'false'){
14830                 success = false;
14831             }
14832         }
14833         var records = [];
14834         for(var i = 0; i < c; i++){
14835                 var n = root[i];
14836             var values = {};
14837             var id = this.getId(n);
14838             for(var j = 0; j < fl; j++){
14839                 f = fi[j];
14840             var v = this.ef[j](n);
14841             if (!f.convert) {
14842                 Roo.log('missing convert for ' + f.name);
14843                 Roo.log(f);
14844                 continue;
14845             }
14846             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14847             }
14848             var record = new Record(values, id);
14849             record.json = n;
14850             records[i] = record;
14851         }
14852         return {
14853             raw : o,
14854             success : success,
14855             records : records,
14856             totalRecords : totalRecords
14857         };
14858     },
14859     // used when loading children.. @see loadDataFromChildren
14860     toLoadData: function(rec)
14861     {
14862         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14863         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14864         return { data : data, total : data.length };
14865         
14866     }
14867 });/*
14868  * Based on:
14869  * Ext JS Library 1.1.1
14870  * Copyright(c) 2006-2007, Ext JS, LLC.
14871  *
14872  * Originally Released Under LGPL - original licence link has changed is not relivant.
14873  *
14874  * Fork - LGPL
14875  * <script type="text/javascript">
14876  */
14877
14878 /**
14879  * @class Roo.data.ArrayReader
14880  * @extends Roo.data.DataReader
14881  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14882  * Each element of that Array represents a row of data fields. The
14883  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14884  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14885  * <p>
14886  * Example code:.
14887  * <pre><code>
14888 var RecordDef = Roo.data.Record.create([
14889     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14890     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14891 ]);
14892 var myReader = new Roo.data.ArrayReader({
14893     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14894 }, RecordDef);
14895 </code></pre>
14896  * <p>
14897  * This would consume an Array like this:
14898  * <pre><code>
14899 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14900   </code></pre>
14901  
14902  * @constructor
14903  * Create a new JsonReader
14904  * @param {Object} meta Metadata configuration options.
14905  * @param {Object|Array} recordType Either an Array of field definition objects
14906  * 
14907  * @cfg {Array} fields Array of field definition objects
14908  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14909  * as specified to {@link Roo.data.Record#create},
14910  * or an {@link Roo.data.Record} object
14911  *
14912  * 
14913  * created using {@link Roo.data.Record#create}.
14914  */
14915 Roo.data.ArrayReader = function(meta, recordType)
14916 {    
14917     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14918 };
14919
14920 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14921     
14922       /**
14923      * Create a data block containing Roo.data.Records from an XML document.
14924      * @param {Object} o An Array of row objects which represents the dataset.
14925      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14926      * a cache of Roo.data.Records.
14927      */
14928     readRecords : function(o)
14929     {
14930         var sid = this.meta ? this.meta.id : null;
14931         var recordType = this.recordType, fields = recordType.prototype.fields;
14932         var records = [];
14933         var root = o;
14934         for(var i = 0; i < root.length; i++){
14935                 var n = root[i];
14936             var values = {};
14937             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14938             for(var j = 0, jlen = fields.length; j < jlen; j++){
14939                 var f = fields.items[j];
14940                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14941                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14942                 v = f.convert(v);
14943                 values[f.name] = v;
14944             }
14945             var record = new recordType(values, id);
14946             record.json = n;
14947             records[records.length] = record;
14948         }
14949         return {
14950             records : records,
14951             totalRecords : records.length
14952         };
14953     },
14954     // used when loading children.. @see loadDataFromChildren
14955     toLoadData: function(rec)
14956     {
14957         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14958         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14959         
14960     }
14961     
14962     
14963 });/*
14964  * - LGPL
14965  * * 
14966  */
14967
14968 /**
14969  * @class Roo.bootstrap.ComboBox
14970  * @extends Roo.bootstrap.TriggerField
14971  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14972  * @cfg {Boolean} append (true|false) default false
14973  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14974  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14975  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14976  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14977  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14978  * @cfg {Boolean} animate default true
14979  * @cfg {Boolean} emptyResultText only for touch device
14980  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14981  * @cfg {String} emptyTitle default ''
14982  * @cfg {Number} width fixed with? experimental
14983  * @constructor
14984  * Create a new ComboBox.
14985  * @param {Object} config Configuration options
14986  */
14987 Roo.bootstrap.ComboBox = function(config){
14988     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14989     this.addEvents({
14990         /**
14991          * @event expand
14992          * Fires when the dropdown list is expanded
14993         * @param {Roo.bootstrap.ComboBox} combo This combo box
14994         */
14995         'expand' : true,
14996         /**
14997          * @event collapse
14998          * Fires when the dropdown list is collapsed
14999         * @param {Roo.bootstrap.ComboBox} combo This combo box
15000         */
15001         'collapse' : true,
15002         /**
15003          * @event beforeselect
15004          * Fires before a list item is selected. Return false to cancel the selection.
15005         * @param {Roo.bootstrap.ComboBox} combo This combo box
15006         * @param {Roo.data.Record} record The data record returned from the underlying store
15007         * @param {Number} index The index of the selected item in the dropdown list
15008         */
15009         'beforeselect' : true,
15010         /**
15011          * @event select
15012          * Fires when a list item is selected
15013         * @param {Roo.bootstrap.ComboBox} combo This combo box
15014         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15015         * @param {Number} index The index of the selected item in the dropdown list
15016         */
15017         'select' : true,
15018         /**
15019          * @event beforequery
15020          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15021          * The event object passed has these properties:
15022         * @param {Roo.bootstrap.ComboBox} combo This combo box
15023         * @param {String} query The query
15024         * @param {Boolean} forceAll true to force "all" query
15025         * @param {Boolean} cancel true to cancel the query
15026         * @param {Object} e The query event object
15027         */
15028         'beforequery': true,
15029          /**
15030          * @event add
15031          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15032         * @param {Roo.bootstrap.ComboBox} combo This combo box
15033         */
15034         'add' : true,
15035         /**
15036          * @event edit
15037          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15038         * @param {Roo.bootstrap.ComboBox} combo This combo box
15039         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15040         */
15041         'edit' : true,
15042         /**
15043          * @event remove
15044          * Fires when the remove value from the combobox array
15045         * @param {Roo.bootstrap.ComboBox} combo This combo box
15046         */
15047         'remove' : true,
15048         /**
15049          * @event afterremove
15050          * Fires when the remove value from the combobox array
15051         * @param {Roo.bootstrap.ComboBox} combo This combo box
15052         */
15053         'afterremove' : true,
15054         /**
15055          * @event specialfilter
15056          * Fires when specialfilter
15057             * @param {Roo.bootstrap.ComboBox} combo This combo box
15058             */
15059         'specialfilter' : true,
15060         /**
15061          * @event tick
15062          * Fires when tick the element
15063             * @param {Roo.bootstrap.ComboBox} combo This combo box
15064             */
15065         'tick' : true,
15066         /**
15067          * @event touchviewdisplay
15068          * Fires when touch view require special display (default is using displayField)
15069             * @param {Roo.bootstrap.ComboBox} combo This combo box
15070             * @param {Object} cfg set html .
15071             */
15072         'touchviewdisplay' : true
15073         
15074     });
15075     
15076     this.item = [];
15077     this.tickItems = [];
15078     
15079     this.selectedIndex = -1;
15080     if(this.mode == 'local'){
15081         if(config.queryDelay === undefined){
15082             this.queryDelay = 10;
15083         }
15084         if(config.minChars === undefined){
15085             this.minChars = 0;
15086         }
15087     }
15088 };
15089
15090 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15091      
15092     /**
15093      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15094      * rendering into an Roo.Editor, defaults to false)
15095      */
15096     /**
15097      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15098      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15099      */
15100     /**
15101      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15102      */
15103     /**
15104      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15105      * the dropdown list (defaults to undefined, with no header element)
15106      */
15107
15108      /**
15109      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15110      */
15111      
15112      /**
15113      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15114      */
15115     listWidth: undefined,
15116     /**
15117      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15118      * mode = 'remote' or 'text' if mode = 'local')
15119      */
15120     displayField: undefined,
15121     
15122     /**
15123      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15124      * mode = 'remote' or 'value' if mode = 'local'). 
15125      * Note: use of a valueField requires the user make a selection
15126      * in order for a value to be mapped.
15127      */
15128     valueField: undefined,
15129     /**
15130      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15131      */
15132     modalTitle : '',
15133     
15134     /**
15135      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15136      * field's data value (defaults to the underlying DOM element's name)
15137      */
15138     hiddenName: undefined,
15139     /**
15140      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15141      */
15142     listClass: '',
15143     /**
15144      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15145      */
15146     selectedClass: 'active',
15147     
15148     /**
15149      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15150      */
15151     shadow:'sides',
15152     /**
15153      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15154      * anchor positions (defaults to 'tl-bl')
15155      */
15156     listAlign: 'tl-bl?',
15157     /**
15158      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15159      */
15160     maxHeight: 300,
15161     /**
15162      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15163      * query specified by the allQuery config option (defaults to 'query')
15164      */
15165     triggerAction: 'query',
15166     /**
15167      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15168      * (defaults to 4, does not apply if editable = false)
15169      */
15170     minChars : 4,
15171     /**
15172      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15173      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15174      */
15175     typeAhead: false,
15176     /**
15177      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15178      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15179      */
15180     queryDelay: 500,
15181     /**
15182      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15183      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15184      */
15185     pageSize: 0,
15186     /**
15187      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15188      * when editable = true (defaults to false)
15189      */
15190     selectOnFocus:false,
15191     /**
15192      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15193      */
15194     queryParam: 'query',
15195     /**
15196      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15197      * when mode = 'remote' (defaults to 'Loading...')
15198      */
15199     loadingText: 'Loading...',
15200     /**
15201      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15202      */
15203     resizable: false,
15204     /**
15205      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15206      */
15207     handleHeight : 8,
15208     /**
15209      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15210      * traditional select (defaults to true)
15211      */
15212     editable: true,
15213     /**
15214      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15215      */
15216     allQuery: '',
15217     /**
15218      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15219      */
15220     mode: 'remote',
15221     /**
15222      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15223      * listWidth has a higher value)
15224      */
15225     minListWidth : 70,
15226     /**
15227      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15228      * allow the user to set arbitrary text into the field (defaults to false)
15229      */
15230     forceSelection:false,
15231     /**
15232      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15233      * if typeAhead = true (defaults to 250)
15234      */
15235     typeAheadDelay : 250,
15236     /**
15237      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15238      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15239      */
15240     valueNotFoundText : undefined,
15241     /**
15242      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15243      */
15244     blockFocus : false,
15245     
15246     /**
15247      * @cfg {Boolean} disableClear Disable showing of clear button.
15248      */
15249     disableClear : false,
15250     /**
15251      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15252      */
15253     alwaysQuery : false,
15254     
15255     /**
15256      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15257      */
15258     multiple : false,
15259     
15260     /**
15261      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15262      */
15263     invalidClass : "has-warning",
15264     
15265     /**
15266      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15267      */
15268     validClass : "has-success",
15269     
15270     /**
15271      * @cfg {Boolean} specialFilter (true|false) special filter default false
15272      */
15273     specialFilter : false,
15274     
15275     /**
15276      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15277      */
15278     mobileTouchView : true,
15279     
15280     /**
15281      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15282      */
15283     useNativeIOS : false,
15284     
15285     /**
15286      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15287      */
15288     mobile_restrict_height : false,
15289     
15290     ios_options : false,
15291     
15292     //private
15293     addicon : false,
15294     editicon: false,
15295     
15296     page: 0,
15297     hasQuery: false,
15298     append: false,
15299     loadNext: false,
15300     autoFocus : true,
15301     tickable : false,
15302     btnPosition : 'right',
15303     triggerList : true,
15304     showToggleBtn : true,
15305     animate : true,
15306     emptyResultText: 'Empty',
15307     triggerText : 'Select',
15308     emptyTitle : '',
15309     width : false,
15310     
15311     // element that contains real text value.. (when hidden is used..)
15312     
15313     getAutoCreate : function()
15314     {   
15315         var cfg = false;
15316         //render
15317         /*
15318          * Render classic select for iso
15319          */
15320         
15321         if(Roo.isIOS && this.useNativeIOS){
15322             cfg = this.getAutoCreateNativeIOS();
15323             return cfg;
15324         }
15325         
15326         /*
15327          * Touch Devices
15328          */
15329         
15330         if(Roo.isTouch && this.mobileTouchView){
15331             cfg = this.getAutoCreateTouchView();
15332             return cfg;;
15333         }
15334         
15335         /*
15336          *  Normal ComboBox
15337          */
15338         if(!this.tickable){
15339             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15340             return cfg;
15341         }
15342         
15343         /*
15344          *  ComboBox with tickable selections
15345          */
15346              
15347         var align = this.labelAlign || this.parentLabelAlign();
15348         
15349         cfg = {
15350             cls : 'form-group roo-combobox-tickable' //input-group
15351         };
15352         
15353         var btn_text_select = '';
15354         var btn_text_done = '';
15355         var btn_text_cancel = '';
15356         
15357         if (this.btn_text_show) {
15358             btn_text_select = 'Select';
15359             btn_text_done = 'Done';
15360             btn_text_cancel = 'Cancel'; 
15361         }
15362         
15363         var buttons = {
15364             tag : 'div',
15365             cls : 'tickable-buttons',
15366             cn : [
15367                 {
15368                     tag : 'button',
15369                     type : 'button',
15370                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15371                     //html : this.triggerText
15372                     html: btn_text_select
15373                 },
15374                 {
15375                     tag : 'button',
15376                     type : 'button',
15377                     name : 'ok',
15378                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15379                     //html : 'Done'
15380                     html: btn_text_done
15381                 },
15382                 {
15383                     tag : 'button',
15384                     type : 'button',
15385                     name : 'cancel',
15386                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15387                     //html : 'Cancel'
15388                     html: btn_text_cancel
15389                 }
15390             ]
15391         };
15392         
15393         if(this.editable){
15394             buttons.cn.unshift({
15395                 tag: 'input',
15396                 cls: 'roo-select2-search-field-input'
15397             });
15398         }
15399         
15400         var _this = this;
15401         
15402         Roo.each(buttons.cn, function(c){
15403             if (_this.size) {
15404                 c.cls += ' btn-' + _this.size;
15405             }
15406
15407             if (_this.disabled) {
15408                 c.disabled = true;
15409             }
15410         });
15411         
15412         var box = {
15413             tag: 'div',
15414             style : 'display: contents',
15415             cn: [
15416                 {
15417                     tag: 'input',
15418                     type : 'hidden',
15419                     cls: 'form-hidden-field'
15420                 },
15421                 {
15422                     tag: 'ul',
15423                     cls: 'roo-select2-choices',
15424                     cn:[
15425                         {
15426                             tag: 'li',
15427                             cls: 'roo-select2-search-field',
15428                             cn: [
15429                                 buttons
15430                             ]
15431                         }
15432                     ]
15433                 }
15434             ]
15435         };
15436         
15437         var combobox = {
15438             cls: 'roo-select2-container input-group roo-select2-container-multi',
15439             cn: [
15440                 
15441                 box
15442 //                {
15443 //                    tag: 'ul',
15444 //                    cls: 'typeahead typeahead-long dropdown-menu',
15445 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15446 //                }
15447             ]
15448         };
15449         
15450         if(this.hasFeedback && !this.allowBlank){
15451             
15452             var feedback = {
15453                 tag: 'span',
15454                 cls: 'glyphicon form-control-feedback'
15455             };
15456
15457             combobox.cn.push(feedback);
15458         }
15459         
15460         
15461         
15462         var indicator = {
15463             tag : 'i',
15464             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15465             tooltip : 'This field is required'
15466         };
15467         if (Roo.bootstrap.version == 4) {
15468             indicator = {
15469                 tag : 'i',
15470                 style : 'display:none'
15471             };
15472         }
15473         if (align ==='left' && this.fieldLabel.length) {
15474             
15475             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15476             
15477             cfg.cn = [
15478                 indicator,
15479                 {
15480                     tag: 'label',
15481                     'for' :  id,
15482                     cls : 'control-label col-form-label',
15483                     html : this.fieldLabel
15484
15485                 },
15486                 {
15487                     cls : "", 
15488                     cn: [
15489                         combobox
15490                     ]
15491                 }
15492
15493             ];
15494             
15495             var labelCfg = cfg.cn[1];
15496             var contentCfg = cfg.cn[2];
15497             
15498
15499             if(this.indicatorpos == 'right'){
15500                 
15501                 cfg.cn = [
15502                     {
15503                         tag: 'label',
15504                         'for' :  id,
15505                         cls : 'control-label col-form-label',
15506                         cn : [
15507                             {
15508                                 tag : 'span',
15509                                 html : this.fieldLabel
15510                             },
15511                             indicator
15512                         ]
15513                     },
15514                     {
15515                         cls : "",
15516                         cn: [
15517                             combobox
15518                         ]
15519                     }
15520
15521                 ];
15522                 
15523                 
15524                 
15525                 labelCfg = cfg.cn[0];
15526                 contentCfg = cfg.cn[1];
15527             
15528             }
15529             
15530             if(this.labelWidth > 12){
15531                 labelCfg.style = "width: " + this.labelWidth + 'px';
15532             }
15533             if(this.width * 1 > 0){
15534                 contentCfg.style = "width: " + this.width + 'px';
15535             }
15536             if(this.labelWidth < 13 && this.labelmd == 0){
15537                 this.labelmd = this.labelWidth;
15538             }
15539             
15540             if(this.labellg > 0){
15541                 labelCfg.cls += ' col-lg-' + this.labellg;
15542                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15543             }
15544             
15545             if(this.labelmd > 0){
15546                 labelCfg.cls += ' col-md-' + this.labelmd;
15547                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15548             }
15549             
15550             if(this.labelsm > 0){
15551                 labelCfg.cls += ' col-sm-' + this.labelsm;
15552                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15553             }
15554             
15555             if(this.labelxs > 0){
15556                 labelCfg.cls += ' col-xs-' + this.labelxs;
15557                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15558             }
15559                 
15560                 
15561         } else if ( this.fieldLabel.length) {
15562 //                Roo.log(" label");
15563                  cfg.cn = [
15564                    indicator,
15565                     {
15566                         tag: 'label',
15567                         //cls : 'input-group-addon',
15568                         html : this.fieldLabel
15569                     },
15570                     combobox
15571                 ];
15572                 
15573                 if(this.indicatorpos == 'right'){
15574                     cfg.cn = [
15575                         {
15576                             tag: 'label',
15577                             //cls : 'input-group-addon',
15578                             html : this.fieldLabel
15579                         },
15580                         indicator,
15581                         combobox
15582                     ];
15583                     
15584                 }
15585
15586         } else {
15587             
15588 //                Roo.log(" no label && no align");
15589                 cfg = combobox
15590                      
15591                 
15592         }
15593          
15594         var settings=this;
15595         ['xs','sm','md','lg'].map(function(size){
15596             if (settings[size]) {
15597                 cfg.cls += ' col-' + size + '-' + settings[size];
15598             }
15599         });
15600         
15601         return cfg;
15602         
15603     },
15604     
15605     _initEventsCalled : false,
15606     
15607     // private
15608     initEvents: function()
15609     {   
15610         if (this._initEventsCalled) { // as we call render... prevent looping...
15611             return;
15612         }
15613         this._initEventsCalled = true;
15614         
15615         if (!this.store) {
15616             throw "can not find store for combo";
15617         }
15618         
15619         this.indicator = this.indicatorEl();
15620         
15621         this.store = Roo.factory(this.store, Roo.data);
15622         this.store.parent = this;
15623         
15624         // if we are building from html. then this element is so complex, that we can not really
15625         // use the rendered HTML.
15626         // so we have to trash and replace the previous code.
15627         if (Roo.XComponent.build_from_html) {
15628             // remove this element....
15629             var e = this.el.dom, k=0;
15630             while (e ) { e = e.previousSibling;  ++k;}
15631
15632             this.el.remove();
15633             
15634             this.el=false;
15635             this.rendered = false;
15636             
15637             this.render(this.parent().getChildContainer(true), k);
15638         }
15639         
15640         if(Roo.isIOS && this.useNativeIOS){
15641             this.initIOSView();
15642             return;
15643         }
15644         
15645         /*
15646          * Touch Devices
15647          */
15648         
15649         if(Roo.isTouch && this.mobileTouchView){
15650             this.initTouchView();
15651             return;
15652         }
15653         
15654         if(this.tickable){
15655             this.initTickableEvents();
15656             return;
15657         }
15658         
15659         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15660         
15661         if(this.hiddenName){
15662             
15663             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15664             
15665             this.hiddenField.dom.value =
15666                 this.hiddenValue !== undefined ? this.hiddenValue :
15667                 this.value !== undefined ? this.value : '';
15668
15669             // prevent input submission
15670             this.el.dom.removeAttribute('name');
15671             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15672              
15673              
15674         }
15675         //if(Roo.isGecko){
15676         //    this.el.dom.setAttribute('autocomplete', 'off');
15677         //}
15678         
15679         var cls = 'x-combo-list';
15680         
15681         //this.list = new Roo.Layer({
15682         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15683         //});
15684         
15685         var _this = this;
15686         
15687         (function(){
15688             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15689             _this.list.setWidth(lw);
15690         }).defer(100);
15691         
15692         this.list.on('mouseover', this.onViewOver, this);
15693         this.list.on('mousemove', this.onViewMove, this);
15694         this.list.on('scroll', this.onViewScroll, this);
15695         
15696         /*
15697         this.list.swallowEvent('mousewheel');
15698         this.assetHeight = 0;
15699
15700         if(this.title){
15701             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15702             this.assetHeight += this.header.getHeight();
15703         }
15704
15705         this.innerList = this.list.createChild({cls:cls+'-inner'});
15706         this.innerList.on('mouseover', this.onViewOver, this);
15707         this.innerList.on('mousemove', this.onViewMove, this);
15708         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15709         
15710         if(this.allowBlank && !this.pageSize && !this.disableClear){
15711             this.footer = this.list.createChild({cls:cls+'-ft'});
15712             this.pageTb = new Roo.Toolbar(this.footer);
15713            
15714         }
15715         if(this.pageSize){
15716             this.footer = this.list.createChild({cls:cls+'-ft'});
15717             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15718                     {pageSize: this.pageSize});
15719             
15720         }
15721         
15722         if (this.pageTb && this.allowBlank && !this.disableClear) {
15723             var _this = this;
15724             this.pageTb.add(new Roo.Toolbar.Fill(), {
15725                 cls: 'x-btn-icon x-btn-clear',
15726                 text: '&#160;',
15727                 handler: function()
15728                 {
15729                     _this.collapse();
15730                     _this.clearValue();
15731                     _this.onSelect(false, -1);
15732                 }
15733             });
15734         }
15735         if (this.footer) {
15736             this.assetHeight += this.footer.getHeight();
15737         }
15738         */
15739             
15740         if(!this.tpl){
15741             this.tpl = Roo.bootstrap.version == 4 ?
15742                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15743                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15744         }
15745
15746         this.view = new Roo.View(this.list, this.tpl, {
15747             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15748         });
15749         //this.view.wrapEl.setDisplayed(false);
15750         this.view.on('click', this.onViewClick, this);
15751         
15752         
15753         this.store.on('beforeload', this.onBeforeLoad, this);
15754         this.store.on('load', this.onLoad, this);
15755         this.store.on('loadexception', this.onLoadException, this);
15756         /*
15757         if(this.resizable){
15758             this.resizer = new Roo.Resizable(this.list,  {
15759                pinned:true, handles:'se'
15760             });
15761             this.resizer.on('resize', function(r, w, h){
15762                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15763                 this.listWidth = w;
15764                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15765                 this.restrictHeight();
15766             }, this);
15767             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15768         }
15769         */
15770         if(!this.editable){
15771             this.editable = true;
15772             this.setEditable(false);
15773         }
15774         
15775         /*
15776         
15777         if (typeof(this.events.add.listeners) != 'undefined') {
15778             
15779             this.addicon = this.wrap.createChild(
15780                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15781        
15782             this.addicon.on('click', function(e) {
15783                 this.fireEvent('add', this);
15784             }, this);
15785         }
15786         if (typeof(this.events.edit.listeners) != 'undefined') {
15787             
15788             this.editicon = this.wrap.createChild(
15789                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15790             if (this.addicon) {
15791                 this.editicon.setStyle('margin-left', '40px');
15792             }
15793             this.editicon.on('click', function(e) {
15794                 
15795                 // we fire even  if inothing is selected..
15796                 this.fireEvent('edit', this, this.lastData );
15797                 
15798             }, this);
15799         }
15800         */
15801         
15802         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15803             "up" : function(e){
15804                 this.inKeyMode = true;
15805                 this.selectPrev();
15806             },
15807
15808             "down" : function(e){
15809                 if(!this.isExpanded()){
15810                     this.onTriggerClick();
15811                 }else{
15812                     this.inKeyMode = true;
15813                     this.selectNext();
15814                 }
15815             },
15816
15817             "enter" : function(e){
15818 //                this.onViewClick();
15819                 //return true;
15820                 this.collapse();
15821                 
15822                 if(this.fireEvent("specialkey", this, e)){
15823                     this.onViewClick(false);
15824                 }
15825                 
15826                 return true;
15827             },
15828
15829             "esc" : function(e){
15830                 this.collapse();
15831             },
15832
15833             "tab" : function(e){
15834                 this.collapse();
15835                 
15836                 if(this.fireEvent("specialkey", this, e)){
15837                     this.onViewClick(false);
15838                 }
15839                 
15840                 return true;
15841             },
15842
15843             scope : this,
15844
15845             doRelay : function(foo, bar, hname){
15846                 if(hname == 'down' || this.scope.isExpanded()){
15847                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15848                 }
15849                 return true;
15850             },
15851
15852             forceKeyDown: true
15853         });
15854         
15855         
15856         this.queryDelay = Math.max(this.queryDelay || 10,
15857                 this.mode == 'local' ? 10 : 250);
15858         
15859         
15860         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15861         
15862         if(this.typeAhead){
15863             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15864         }
15865         if(this.editable !== false){
15866             this.inputEl().on("keyup", this.onKeyUp, this);
15867         }
15868         if(this.forceSelection){
15869             this.inputEl().on('blur', this.doForce, this);
15870         }
15871         
15872         if(this.multiple){
15873             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15874             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15875         }
15876     },
15877     
15878     initTickableEvents: function()
15879     {   
15880         this.createList();
15881         
15882         if(this.hiddenName){
15883             
15884             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15885             
15886             this.hiddenField.dom.value =
15887                 this.hiddenValue !== undefined ? this.hiddenValue :
15888                 this.value !== undefined ? this.value : '';
15889
15890             // prevent input submission
15891             this.el.dom.removeAttribute('name');
15892             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15893              
15894              
15895         }
15896         
15897 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15898         
15899         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15900         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15901         if(this.triggerList){
15902             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15903         }
15904          
15905         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15906         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15907         
15908         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15909         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15910         
15911         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15912         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15913         
15914         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15915         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15916         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15917         
15918         this.okBtn.hide();
15919         this.cancelBtn.hide();
15920         
15921         var _this = this;
15922         
15923         (function(){
15924             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15925             _this.list.setWidth(lw);
15926         }).defer(100);
15927         
15928         this.list.on('mouseover', this.onViewOver, this);
15929         this.list.on('mousemove', this.onViewMove, this);
15930         
15931         this.list.on('scroll', this.onViewScroll, this);
15932         
15933         if(!this.tpl){
15934             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15935                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15936         }
15937
15938         this.view = new Roo.View(this.list, this.tpl, {
15939             singleSelect:true,
15940             tickable:true,
15941             parent:this,
15942             store: this.store,
15943             selectedClass: this.selectedClass
15944         });
15945         
15946         //this.view.wrapEl.setDisplayed(false);
15947         this.view.on('click', this.onViewClick, this);
15948         
15949         
15950         
15951         this.store.on('beforeload', this.onBeforeLoad, this);
15952         this.store.on('load', this.onLoad, this);
15953         this.store.on('loadexception', this.onLoadException, this);
15954         
15955         if(this.editable){
15956             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15957                 "up" : function(e){
15958                     this.inKeyMode = true;
15959                     this.selectPrev();
15960                 },
15961
15962                 "down" : function(e){
15963                     this.inKeyMode = true;
15964                     this.selectNext();
15965                 },
15966
15967                 "enter" : function(e){
15968                     if(this.fireEvent("specialkey", this, e)){
15969                         this.onViewClick(false);
15970                     }
15971                     
15972                     return true;
15973                 },
15974
15975                 "esc" : function(e){
15976                     this.onTickableFooterButtonClick(e, false, false);
15977                 },
15978
15979                 "tab" : function(e){
15980                     this.fireEvent("specialkey", this, e);
15981                     
15982                     this.onTickableFooterButtonClick(e, false, false);
15983                     
15984                     return true;
15985                 },
15986
15987                 scope : this,
15988
15989                 doRelay : function(e, fn, key){
15990                     if(this.scope.isExpanded()){
15991                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15992                     }
15993                     return true;
15994                 },
15995
15996                 forceKeyDown: true
15997             });
15998         }
15999         
16000         this.queryDelay = Math.max(this.queryDelay || 10,
16001                 this.mode == 'local' ? 10 : 250);
16002         
16003         
16004         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16005         
16006         if(this.typeAhead){
16007             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16008         }
16009         
16010         if(this.editable !== false){
16011             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16012         }
16013         
16014         this.indicator = this.indicatorEl();
16015         
16016         if(this.indicator){
16017             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16018             this.indicator.hide();
16019         }
16020         
16021     },
16022
16023     onDestroy : function(){
16024         if(this.view){
16025             this.view.setStore(null);
16026             this.view.el.removeAllListeners();
16027             this.view.el.remove();
16028             this.view.purgeListeners();
16029         }
16030         if(this.list){
16031             this.list.dom.innerHTML  = '';
16032         }
16033         
16034         if(this.store){
16035             this.store.un('beforeload', this.onBeforeLoad, this);
16036             this.store.un('load', this.onLoad, this);
16037             this.store.un('loadexception', this.onLoadException, this);
16038         }
16039         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16040     },
16041
16042     // private
16043     fireKey : function(e){
16044         if(e.isNavKeyPress() && !this.list.isVisible()){
16045             this.fireEvent("specialkey", this, e);
16046         }
16047     },
16048
16049     // private
16050     onResize: function(w, h)
16051     {
16052         
16053         
16054 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16055 //        
16056 //        if(typeof w != 'number'){
16057 //            // we do not handle it!?!?
16058 //            return;
16059 //        }
16060 //        var tw = this.trigger.getWidth();
16061 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16062 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16063 //        var x = w - tw;
16064 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16065 //            
16066 //        //this.trigger.setStyle('left', x+'px');
16067 //        
16068 //        if(this.list && this.listWidth === undefined){
16069 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16070 //            this.list.setWidth(lw);
16071 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16072 //        }
16073         
16074     
16075         
16076     },
16077
16078     /**
16079      * Allow or prevent the user from directly editing the field text.  If false is passed,
16080      * the user will only be able to select from the items defined in the dropdown list.  This method
16081      * is the runtime equivalent of setting the 'editable' config option at config time.
16082      * @param {Boolean} value True to allow the user to directly edit the field text
16083      */
16084     setEditable : function(value){
16085         if(value == this.editable){
16086             return;
16087         }
16088         this.editable = value;
16089         if(!value){
16090             this.inputEl().dom.setAttribute('readOnly', true);
16091             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16092             this.inputEl().addClass('x-combo-noedit');
16093         }else{
16094             this.inputEl().dom.setAttribute('readOnly', false);
16095             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16096             this.inputEl().removeClass('x-combo-noedit');
16097         }
16098     },
16099
16100     // private
16101     
16102     onBeforeLoad : function(combo,opts){
16103         if(!this.hasFocus){
16104             return;
16105         }
16106          if (!opts.add) {
16107             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16108          }
16109         this.restrictHeight();
16110         this.selectedIndex = -1;
16111     },
16112
16113     // private
16114     onLoad : function(){
16115         
16116         this.hasQuery = false;
16117         
16118         if(!this.hasFocus){
16119             return;
16120         }
16121         
16122         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16123             this.loading.hide();
16124         }
16125         
16126         if(this.store.getCount() > 0){
16127             
16128             this.expand();
16129             this.restrictHeight();
16130             if(this.lastQuery == this.allQuery){
16131                 if(this.editable && !this.tickable){
16132                     this.inputEl().dom.select();
16133                 }
16134                 
16135                 if(
16136                     !this.selectByValue(this.value, true) &&
16137                     this.autoFocus && 
16138                     (
16139                         !this.store.lastOptions ||
16140                         typeof(this.store.lastOptions.add) == 'undefined' || 
16141                         this.store.lastOptions.add != true
16142                     )
16143                 ){
16144                     this.select(0, true);
16145                 }
16146             }else{
16147                 if(this.autoFocus){
16148                     this.selectNext();
16149                 }
16150                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16151                     this.taTask.delay(this.typeAheadDelay);
16152                 }
16153             }
16154         }else{
16155             this.onEmptyResults();
16156         }
16157         
16158         //this.el.focus();
16159     },
16160     // private
16161     onLoadException : function()
16162     {
16163         this.hasQuery = false;
16164         
16165         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16166             this.loading.hide();
16167         }
16168         
16169         if(this.tickable && this.editable){
16170             return;
16171         }
16172         
16173         this.collapse();
16174         // only causes errors at present
16175         //Roo.log(this.store.reader.jsonData);
16176         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16177             // fixme
16178             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16179         //}
16180         
16181         
16182     },
16183     // private
16184     onTypeAhead : function(){
16185         if(this.store.getCount() > 0){
16186             var r = this.store.getAt(0);
16187             var newValue = r.data[this.displayField];
16188             var len = newValue.length;
16189             var selStart = this.getRawValue().length;
16190             
16191             if(selStart != len){
16192                 this.setRawValue(newValue);
16193                 this.selectText(selStart, newValue.length);
16194             }
16195         }
16196     },
16197
16198     // private
16199     onSelect : function(record, index){
16200         
16201         if(this.fireEvent('beforeselect', this, record, index) !== false){
16202         
16203             this.setFromData(index > -1 ? record.data : false);
16204             
16205             this.collapse();
16206             this.fireEvent('select', this, record, index);
16207         }
16208     },
16209
16210     /**
16211      * Returns the currently selected field value or empty string if no value is set.
16212      * @return {String} value The selected value
16213      */
16214     getValue : function()
16215     {
16216         if(Roo.isIOS && this.useNativeIOS){
16217             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16218         }
16219         
16220         if(this.multiple){
16221             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16222         }
16223         
16224         if(this.valueField){
16225             return typeof this.value != 'undefined' ? this.value : '';
16226         }else{
16227             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16228         }
16229     },
16230     
16231     getRawValue : function()
16232     {
16233         if(Roo.isIOS && this.useNativeIOS){
16234             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16235         }
16236         
16237         var v = this.inputEl().getValue();
16238         
16239         return v;
16240     },
16241
16242     /**
16243      * Clears any text/value currently set in the field
16244      */
16245     clearValue : function(){
16246         
16247         if(this.hiddenField){
16248             this.hiddenField.dom.value = '';
16249         }
16250         this.value = '';
16251         this.setRawValue('');
16252         this.lastSelectionText = '';
16253         this.lastData = false;
16254         
16255         var close = this.closeTriggerEl();
16256         
16257         if(close){
16258             close.hide();
16259         }
16260         
16261         this.validate();
16262         
16263     },
16264
16265     /**
16266      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16267      * will be displayed in the field.  If the value does not match the data value of an existing item,
16268      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16269      * Otherwise the field will be blank (although the value will still be set).
16270      * @param {String} value The value to match
16271      */
16272     setValue : function(v)
16273     {
16274         if(Roo.isIOS && this.useNativeIOS){
16275             this.setIOSValue(v);
16276             return;
16277         }
16278         
16279         if(this.multiple){
16280             this.syncValue();
16281             return;
16282         }
16283         
16284         var text = v;
16285         if(this.valueField){
16286             var r = this.findRecord(this.valueField, v);
16287             if(r){
16288                 text = r.data[this.displayField];
16289             }else if(this.valueNotFoundText !== undefined){
16290                 text = this.valueNotFoundText;
16291             }
16292         }
16293         this.lastSelectionText = text;
16294         if(this.hiddenField){
16295             this.hiddenField.dom.value = v;
16296         }
16297         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16298         this.value = v;
16299         
16300         var close = this.closeTriggerEl();
16301         
16302         if(close){
16303             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16304         }
16305         
16306         this.validate();
16307     },
16308     /**
16309      * @property {Object} the last set data for the element
16310      */
16311     
16312     lastData : false,
16313     /**
16314      * Sets the value of the field based on a object which is related to the record format for the store.
16315      * @param {Object} value the value to set as. or false on reset?
16316      */
16317     setFromData : function(o){
16318         
16319         if(this.multiple){
16320             this.addItem(o);
16321             return;
16322         }
16323             
16324         var dv = ''; // display value
16325         var vv = ''; // value value..
16326         this.lastData = o;
16327         if (this.displayField) {
16328             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16329         } else {
16330             // this is an error condition!!!
16331             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16332         }
16333         
16334         if(this.valueField){
16335             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16336         }
16337         
16338         var close = this.closeTriggerEl();
16339         
16340         if(close){
16341             if(dv.length || vv * 1 > 0){
16342                 close.show() ;
16343                 this.blockFocus=true;
16344             } else {
16345                 close.hide();
16346             }             
16347         }
16348         
16349         if(this.hiddenField){
16350             this.hiddenField.dom.value = vv;
16351             
16352             this.lastSelectionText = dv;
16353             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16354             this.value = vv;
16355             return;
16356         }
16357         // no hidden field.. - we store the value in 'value', but still display
16358         // display field!!!!
16359         this.lastSelectionText = dv;
16360         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16361         this.value = vv;
16362         
16363         
16364         
16365     },
16366     // private
16367     reset : function(){
16368         // overridden so that last data is reset..
16369         
16370         if(this.multiple){
16371             this.clearItem();
16372             return;
16373         }
16374         
16375         this.setValue(this.originalValue);
16376         //this.clearInvalid();
16377         this.lastData = false;
16378         if (this.view) {
16379             this.view.clearSelections();
16380         }
16381         
16382         this.validate();
16383     },
16384     // private
16385     findRecord : function(prop, value){
16386         var record;
16387         if(this.store.getCount() > 0){
16388             this.store.each(function(r){
16389                 if(r.data[prop] == value){
16390                     record = r;
16391                     return false;
16392                 }
16393                 return true;
16394             });
16395         }
16396         return record;
16397     },
16398     
16399     getName: function()
16400     {
16401         // returns hidden if it's set..
16402         if (!this.rendered) {return ''};
16403         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16404         
16405     },
16406     // private
16407     onViewMove : function(e, t){
16408         this.inKeyMode = false;
16409     },
16410
16411     // private
16412     onViewOver : function(e, t){
16413         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16414             return;
16415         }
16416         var item = this.view.findItemFromChild(t);
16417         
16418         if(item){
16419             var index = this.view.indexOf(item);
16420             this.select(index, false);
16421         }
16422     },
16423
16424     // private
16425     onViewClick : function(view, doFocus, el, e)
16426     {
16427         var index = this.view.getSelectedIndexes()[0];
16428         
16429         var r = this.store.getAt(index);
16430         
16431         if(this.tickable){
16432             
16433             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16434                 return;
16435             }
16436             
16437             var rm = false;
16438             var _this = this;
16439             
16440             Roo.each(this.tickItems, function(v,k){
16441                 
16442                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16443                     Roo.log(v);
16444                     _this.tickItems.splice(k, 1);
16445                     
16446                     if(typeof(e) == 'undefined' && view == false){
16447                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16448                     }
16449                     
16450                     rm = true;
16451                     return;
16452                 }
16453             });
16454             
16455             if(rm){
16456                 return;
16457             }
16458             
16459             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16460                 this.tickItems.push(r.data);
16461             }
16462             
16463             if(typeof(e) == 'undefined' && view == false){
16464                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16465             }
16466                     
16467             return;
16468         }
16469         
16470         if(r){
16471             this.onSelect(r, index);
16472         }
16473         if(doFocus !== false && !this.blockFocus){
16474             this.inputEl().focus();
16475         }
16476     },
16477
16478     // private
16479     restrictHeight : function(){
16480         //this.innerList.dom.style.height = '';
16481         //var inner = this.innerList.dom;
16482         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16483         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16484         //this.list.beginUpdate();
16485         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16486         this.list.alignTo(this.inputEl(), this.listAlign);
16487         this.list.alignTo(this.inputEl(), this.listAlign);
16488         //this.list.endUpdate();
16489     },
16490
16491     // private
16492     onEmptyResults : function(){
16493         
16494         if(this.tickable && this.editable){
16495             this.hasFocus = false;
16496             this.restrictHeight();
16497             return;
16498         }
16499         
16500         this.collapse();
16501     },
16502
16503     /**
16504      * Returns true if the dropdown list is expanded, else false.
16505      */
16506     isExpanded : function(){
16507         return this.list.isVisible();
16508     },
16509
16510     /**
16511      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16512      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16513      * @param {String} value The data value of the item to select
16514      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16515      * selected item if it is not currently in view (defaults to true)
16516      * @return {Boolean} True if the value matched an item in the list, else false
16517      */
16518     selectByValue : function(v, scrollIntoView){
16519         if(v !== undefined && v !== null){
16520             var r = this.findRecord(this.valueField || this.displayField, v);
16521             if(r){
16522                 this.select(this.store.indexOf(r), scrollIntoView);
16523                 return true;
16524             }
16525         }
16526         return false;
16527     },
16528
16529     /**
16530      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16531      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16532      * @param {Number} index The zero-based index of the list item to select
16533      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16534      * selected item if it is not currently in view (defaults to true)
16535      */
16536     select : function(index, scrollIntoView){
16537         this.selectedIndex = index;
16538         this.view.select(index);
16539         if(scrollIntoView !== false){
16540             var el = this.view.getNode(index);
16541             /*
16542              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16543              */
16544             if(el){
16545                 this.list.scrollChildIntoView(el, false);
16546             }
16547         }
16548     },
16549
16550     // private
16551     selectNext : function(){
16552         var ct = this.store.getCount();
16553         if(ct > 0){
16554             if(this.selectedIndex == -1){
16555                 this.select(0);
16556             }else if(this.selectedIndex < ct-1){
16557                 this.select(this.selectedIndex+1);
16558             }
16559         }
16560     },
16561
16562     // private
16563     selectPrev : function(){
16564         var ct = this.store.getCount();
16565         if(ct > 0){
16566             if(this.selectedIndex == -1){
16567                 this.select(0);
16568             }else if(this.selectedIndex != 0){
16569                 this.select(this.selectedIndex-1);
16570             }
16571         }
16572     },
16573
16574     // private
16575     onKeyUp : function(e){
16576         if(this.editable !== false && !e.isSpecialKey()){
16577             this.lastKey = e.getKey();
16578             this.dqTask.delay(this.queryDelay);
16579         }
16580     },
16581
16582     // private
16583     validateBlur : function(){
16584         return !this.list || !this.list.isVisible();   
16585     },
16586
16587     // private
16588     initQuery : function(){
16589         
16590         var v = this.getRawValue();
16591         
16592         if(this.tickable && this.editable){
16593             v = this.tickableInputEl().getValue();
16594         }
16595         
16596         this.doQuery(v);
16597     },
16598
16599     // private
16600     doForce : function(){
16601         if(this.inputEl().dom.value.length > 0){
16602             this.inputEl().dom.value =
16603                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16604              
16605         }
16606     },
16607
16608     /**
16609      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16610      * query allowing the query action to be canceled if needed.
16611      * @param {String} query The SQL query to execute
16612      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16613      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16614      * saved in the current store (defaults to false)
16615      */
16616     doQuery : function(q, forceAll){
16617         
16618         if(q === undefined || q === null){
16619             q = '';
16620         }
16621         var qe = {
16622             query: q,
16623             forceAll: forceAll,
16624             combo: this,
16625             cancel:false
16626         };
16627         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16628             return false;
16629         }
16630         q = qe.query;
16631         
16632         forceAll = qe.forceAll;
16633         if(forceAll === true || (q.length >= this.minChars)){
16634             
16635             this.hasQuery = true;
16636             
16637             if(this.lastQuery != q || this.alwaysQuery){
16638                 this.lastQuery = q;
16639                 if(this.mode == 'local'){
16640                     this.selectedIndex = -1;
16641                     if(forceAll){
16642                         this.store.clearFilter();
16643                     }else{
16644                         
16645                         if(this.specialFilter){
16646                             this.fireEvent('specialfilter', this);
16647                             this.onLoad();
16648                             return;
16649                         }
16650                         
16651                         this.store.filter(this.displayField, q);
16652                     }
16653                     
16654                     this.store.fireEvent("datachanged", this.store);
16655                     
16656                     this.onLoad();
16657                     
16658                     
16659                 }else{
16660                     
16661                     this.store.baseParams[this.queryParam] = q;
16662                     
16663                     var options = {params : this.getParams(q)};
16664                     
16665                     if(this.loadNext){
16666                         options.add = true;
16667                         options.params.start = this.page * this.pageSize;
16668                     }
16669                     
16670                     this.store.load(options);
16671                     
16672                     /*
16673                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16674                      *  we should expand the list on onLoad
16675                      *  so command out it
16676                      */
16677 //                    this.expand();
16678                 }
16679             }else{
16680                 this.selectedIndex = -1;
16681                 this.onLoad();   
16682             }
16683         }
16684         
16685         this.loadNext = false;
16686     },
16687     
16688     // private
16689     getParams : function(q){
16690         var p = {};
16691         //p[this.queryParam] = q;
16692         
16693         if(this.pageSize){
16694             p.start = 0;
16695             p.limit = this.pageSize;
16696         }
16697         return p;
16698     },
16699
16700     /**
16701      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16702      */
16703     collapse : function(){
16704         if(!this.isExpanded()){
16705             return;
16706         }
16707         
16708         this.list.hide();
16709         
16710         this.hasFocus = false;
16711         
16712         if(this.tickable){
16713             this.okBtn.hide();
16714             this.cancelBtn.hide();
16715             this.trigger.show();
16716             
16717             if(this.editable){
16718                 this.tickableInputEl().dom.value = '';
16719                 this.tickableInputEl().blur();
16720             }
16721             
16722         }
16723         
16724         Roo.get(document).un('mousedown', this.collapseIf, this);
16725         Roo.get(document).un('mousewheel', this.collapseIf, this);
16726         if (!this.editable) {
16727             Roo.get(document).un('keydown', this.listKeyPress, this);
16728         }
16729         this.fireEvent('collapse', this);
16730         
16731         this.validate();
16732     },
16733
16734     // private
16735     collapseIf : function(e){
16736         var in_combo  = e.within(this.el);
16737         var in_list =  e.within(this.list);
16738         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16739         
16740         if (in_combo || in_list || is_list) {
16741             //e.stopPropagation();
16742             return;
16743         }
16744         
16745         if(this.tickable){
16746             this.onTickableFooterButtonClick(e, false, false);
16747         }
16748
16749         this.collapse();
16750         
16751     },
16752
16753     /**
16754      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16755      */
16756     expand : function(){
16757        
16758         if(this.isExpanded() || !this.hasFocus){
16759             return;
16760         }
16761         
16762         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16763         this.list.setWidth(lw);
16764         
16765         Roo.log('expand');
16766         
16767         this.list.show();
16768         
16769         this.restrictHeight();
16770         
16771         if(this.tickable){
16772             
16773             this.tickItems = Roo.apply([], this.item);
16774             
16775             this.okBtn.show();
16776             this.cancelBtn.show();
16777             this.trigger.hide();
16778             
16779             if(this.editable){
16780                 this.tickableInputEl().focus();
16781             }
16782             
16783         }
16784         
16785         Roo.get(document).on('mousedown', this.collapseIf, this);
16786         Roo.get(document).on('mousewheel', this.collapseIf, this);
16787         if (!this.editable) {
16788             Roo.get(document).on('keydown', this.listKeyPress, this);
16789         }
16790         
16791         this.fireEvent('expand', this);
16792     },
16793
16794     // private
16795     // Implements the default empty TriggerField.onTriggerClick function
16796     onTriggerClick : function(e)
16797     {
16798         Roo.log('trigger click');
16799         
16800         if(this.disabled || !this.triggerList){
16801             return;
16802         }
16803         
16804         this.page = 0;
16805         this.loadNext = false;
16806         
16807         if(this.isExpanded()){
16808             this.collapse();
16809             if (!this.blockFocus) {
16810                 this.inputEl().focus();
16811             }
16812             
16813         }else {
16814             this.hasFocus = true;
16815             if(this.triggerAction == 'all') {
16816                 this.doQuery(this.allQuery, true);
16817             } else {
16818                 this.doQuery(this.getRawValue());
16819             }
16820             if (!this.blockFocus) {
16821                 this.inputEl().focus();
16822             }
16823         }
16824     },
16825     
16826     onTickableTriggerClick : function(e)
16827     {
16828         if(this.disabled){
16829             return;
16830         }
16831         
16832         this.page = 0;
16833         this.loadNext = false;
16834         this.hasFocus = true;
16835         
16836         if(this.triggerAction == 'all') {
16837             this.doQuery(this.allQuery, true);
16838         } else {
16839             this.doQuery(this.getRawValue());
16840         }
16841     },
16842     
16843     onSearchFieldClick : function(e)
16844     {
16845         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16846             this.onTickableFooterButtonClick(e, false, false);
16847             return;
16848         }
16849         
16850         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16851             return;
16852         }
16853         
16854         this.page = 0;
16855         this.loadNext = false;
16856         this.hasFocus = true;
16857         
16858         if(this.triggerAction == 'all') {
16859             this.doQuery(this.allQuery, true);
16860         } else {
16861             this.doQuery(this.getRawValue());
16862         }
16863     },
16864     
16865     listKeyPress : function(e)
16866     {
16867         //Roo.log('listkeypress');
16868         // scroll to first matching element based on key pres..
16869         if (e.isSpecialKey()) {
16870             return false;
16871         }
16872         var k = String.fromCharCode(e.getKey()).toUpperCase();
16873         //Roo.log(k);
16874         var match  = false;
16875         var csel = this.view.getSelectedNodes();
16876         var cselitem = false;
16877         if (csel.length) {
16878             var ix = this.view.indexOf(csel[0]);
16879             cselitem  = this.store.getAt(ix);
16880             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16881                 cselitem = false;
16882             }
16883             
16884         }
16885         
16886         this.store.each(function(v) { 
16887             if (cselitem) {
16888                 // start at existing selection.
16889                 if (cselitem.id == v.id) {
16890                     cselitem = false;
16891                 }
16892                 return true;
16893             }
16894                 
16895             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16896                 match = this.store.indexOf(v);
16897                 return false;
16898             }
16899             return true;
16900         }, this);
16901         
16902         if (match === false) {
16903             return true; // no more action?
16904         }
16905         // scroll to?
16906         this.view.select(match);
16907         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16908         sn.scrollIntoView(sn.dom.parentNode, false);
16909     },
16910     
16911     onViewScroll : function(e, t){
16912         
16913         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){
16914             return;
16915         }
16916         
16917         this.hasQuery = true;
16918         
16919         this.loading = this.list.select('.loading', true).first();
16920         
16921         if(this.loading === null){
16922             this.list.createChild({
16923                 tag: 'div',
16924                 cls: 'loading roo-select2-more-results roo-select2-active',
16925                 html: 'Loading more results...'
16926             });
16927             
16928             this.loading = this.list.select('.loading', true).first();
16929             
16930             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16931             
16932             this.loading.hide();
16933         }
16934         
16935         this.loading.show();
16936         
16937         var _combo = this;
16938         
16939         this.page++;
16940         this.loadNext = true;
16941         
16942         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16943         
16944         return;
16945     },
16946     
16947     addItem : function(o)
16948     {   
16949         var dv = ''; // display value
16950         
16951         if (this.displayField) {
16952             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16953         } else {
16954             // this is an error condition!!!
16955             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16956         }
16957         
16958         if(!dv.length){
16959             return;
16960         }
16961         
16962         var choice = this.choices.createChild({
16963             tag: 'li',
16964             cls: 'roo-select2-search-choice',
16965             cn: [
16966                 {
16967                     tag: 'div',
16968                     html: dv
16969                 },
16970                 {
16971                     tag: 'a',
16972                     href: '#',
16973                     cls: 'roo-select2-search-choice-close fa fa-times',
16974                     tabindex: '-1'
16975                 }
16976             ]
16977             
16978         }, this.searchField);
16979         
16980         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16981         
16982         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16983         
16984         this.item.push(o);
16985         
16986         this.lastData = o;
16987         
16988         this.syncValue();
16989         
16990         this.inputEl().dom.value = '';
16991         
16992         this.validate();
16993     },
16994     
16995     onRemoveItem : function(e, _self, o)
16996     {
16997         e.preventDefault();
16998         
16999         this.lastItem = Roo.apply([], this.item);
17000         
17001         var index = this.item.indexOf(o.data) * 1;
17002         
17003         if( index < 0){
17004             Roo.log('not this item?!');
17005             return;
17006         }
17007         
17008         this.item.splice(index, 1);
17009         o.item.remove();
17010         
17011         this.syncValue();
17012         
17013         this.fireEvent('remove', this, e);
17014         
17015         this.validate();
17016         
17017     },
17018     
17019     syncValue : function()
17020     {
17021         if(!this.item.length){
17022             this.clearValue();
17023             return;
17024         }
17025             
17026         var value = [];
17027         var _this = this;
17028         Roo.each(this.item, function(i){
17029             if(_this.valueField){
17030                 value.push(i[_this.valueField]);
17031                 return;
17032             }
17033
17034             value.push(i);
17035         });
17036
17037         this.value = value.join(',');
17038
17039         if(this.hiddenField){
17040             this.hiddenField.dom.value = this.value;
17041         }
17042         
17043         this.store.fireEvent("datachanged", this.store);
17044         
17045         this.validate();
17046     },
17047     
17048     clearItem : function()
17049     {
17050         if(!this.multiple){
17051             return;
17052         }
17053         
17054         this.item = [];
17055         
17056         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17057            c.remove();
17058         });
17059         
17060         this.syncValue();
17061         
17062         this.validate();
17063         
17064         if(this.tickable && !Roo.isTouch){
17065             this.view.refresh();
17066         }
17067     },
17068     
17069     inputEl: function ()
17070     {
17071         if(Roo.isIOS && this.useNativeIOS){
17072             return this.el.select('select.roo-ios-select', true).first();
17073         }
17074         
17075         if(Roo.isTouch && this.mobileTouchView){
17076             return this.el.select('input.form-control',true).first();
17077         }
17078         
17079         if(this.tickable){
17080             return this.searchField;
17081         }
17082         
17083         return this.el.select('input.form-control',true).first();
17084     },
17085     
17086     onTickableFooterButtonClick : function(e, btn, el)
17087     {
17088         e.preventDefault();
17089         
17090         this.lastItem = Roo.apply([], this.item);
17091         
17092         if(btn && btn.name == 'cancel'){
17093             this.tickItems = Roo.apply([], this.item);
17094             this.collapse();
17095             return;
17096         }
17097         
17098         this.clearItem();
17099         
17100         var _this = this;
17101         
17102         Roo.each(this.tickItems, function(o){
17103             _this.addItem(o);
17104         });
17105         
17106         this.collapse();
17107         
17108     },
17109     
17110     validate : function()
17111     {
17112         if(this.getVisibilityEl().hasClass('hidden')){
17113             return true;
17114         }
17115         
17116         var v = this.getRawValue();
17117         
17118         if(this.multiple){
17119             v = this.getValue();
17120         }
17121         
17122         if(this.disabled || this.allowBlank || v.length){
17123             this.markValid();
17124             return true;
17125         }
17126         
17127         this.markInvalid();
17128         return false;
17129     },
17130     
17131     tickableInputEl : function()
17132     {
17133         if(!this.tickable || !this.editable){
17134             return this.inputEl();
17135         }
17136         
17137         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17138     },
17139     
17140     
17141     getAutoCreateTouchView : function()
17142     {
17143         var id = Roo.id();
17144         
17145         var cfg = {
17146             cls: 'form-group' //input-group
17147         };
17148         
17149         var input =  {
17150             tag: 'input',
17151             id : id,
17152             type : this.inputType,
17153             cls : 'form-control x-combo-noedit',
17154             autocomplete: 'new-password',
17155             placeholder : this.placeholder || '',
17156             readonly : true
17157         };
17158         
17159         if (this.name) {
17160             input.name = this.name;
17161         }
17162         
17163         if (this.size) {
17164             input.cls += ' input-' + this.size;
17165         }
17166         
17167         if (this.disabled) {
17168             input.disabled = true;
17169         }
17170         
17171         var inputblock = {
17172             cls : 'roo-combobox-wrap',
17173             cn : [
17174                 input
17175             ]
17176         };
17177         
17178         if(this.before){
17179             inputblock.cls += ' input-group';
17180             
17181             inputblock.cn.unshift({
17182                 tag :'span',
17183                 cls : 'input-group-addon input-group-prepend input-group-text',
17184                 html : this.before
17185             });
17186         }
17187         
17188         if(this.removable && !this.multiple){
17189             inputblock.cls += ' roo-removable';
17190             
17191             inputblock.cn.push({
17192                 tag: 'button',
17193                 html : 'x',
17194                 cls : 'roo-combo-removable-btn close'
17195             });
17196         }
17197
17198         if(this.hasFeedback && !this.allowBlank){
17199             
17200             inputblock.cls += ' has-feedback';
17201             
17202             inputblock.cn.push({
17203                 tag: 'span',
17204                 cls: 'glyphicon form-control-feedback'
17205             });
17206             
17207         }
17208         
17209         if (this.after) {
17210             
17211             inputblock.cls += (this.before) ? '' : ' input-group';
17212             
17213             inputblock.cn.push({
17214                 tag :'span',
17215                 cls : 'input-group-addon input-group-append input-group-text',
17216                 html : this.after
17217             });
17218         }
17219
17220         
17221         var ibwrap = inputblock;
17222         
17223         if(this.multiple){
17224             ibwrap = {
17225                 tag: 'ul',
17226                 cls: 'roo-select2-choices',
17227                 cn:[
17228                     {
17229                         tag: 'li',
17230                         cls: 'roo-select2-search-field',
17231                         cn: [
17232
17233                             inputblock
17234                         ]
17235                     }
17236                 ]
17237             };
17238         
17239             
17240         }
17241         
17242         var combobox = {
17243             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17244             cn: [
17245                 {
17246                     tag: 'input',
17247                     type : 'hidden',
17248                     cls: 'form-hidden-field'
17249                 },
17250                 ibwrap
17251             ]
17252         };
17253         
17254         if(!this.multiple && this.showToggleBtn){
17255             
17256             var caret = {
17257                 cls: 'caret'
17258             };
17259             
17260             if (this.caret != false) {
17261                 caret = {
17262                      tag: 'i',
17263                      cls: 'fa fa-' + this.caret
17264                 };
17265                 
17266             }
17267             
17268             combobox.cn.push({
17269                 tag :'span',
17270                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17271                 cn : [
17272                     Roo.bootstrap.version == 3 ? caret : '',
17273                     {
17274                         tag: 'span',
17275                         cls: 'combobox-clear',
17276                         cn  : [
17277                             {
17278                                 tag : 'i',
17279                                 cls: 'icon-remove'
17280                             }
17281                         ]
17282                     }
17283                 ]
17284
17285             })
17286         }
17287         
17288         if(this.multiple){
17289             combobox.cls += ' roo-select2-container-multi';
17290         }
17291         
17292         var align = this.labelAlign || this.parentLabelAlign();
17293         
17294         if (align ==='left' && this.fieldLabel.length) {
17295
17296             cfg.cn = [
17297                 {
17298                    tag : 'i',
17299                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17300                    tooltip : 'This field is required'
17301                 },
17302                 {
17303                     tag: 'label',
17304                     cls : 'control-label col-form-label',
17305                     html : this.fieldLabel
17306
17307                 },
17308                 {
17309                     cls : 'roo-combobox-wrap ', 
17310                     cn: [
17311                         combobox
17312                     ]
17313                 }
17314             ];
17315             
17316             var labelCfg = cfg.cn[1];
17317             var contentCfg = cfg.cn[2];
17318             
17319
17320             if(this.indicatorpos == 'right'){
17321                 cfg.cn = [
17322                     {
17323                         tag: 'label',
17324                         'for' :  id,
17325                         cls : 'control-label col-form-label',
17326                         cn : [
17327                             {
17328                                 tag : 'span',
17329                                 html : this.fieldLabel
17330                             },
17331                             {
17332                                 tag : 'i',
17333                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17334                                 tooltip : 'This field is required'
17335                             }
17336                         ]
17337                     },
17338                     {
17339                         cls : "roo-combobox-wrap ",
17340                         cn: [
17341                             combobox
17342                         ]
17343                     }
17344
17345                 ];
17346                 
17347                 labelCfg = cfg.cn[0];
17348                 contentCfg = cfg.cn[1];
17349             }
17350             
17351            
17352             
17353             if(this.labelWidth > 12){
17354                 labelCfg.style = "width: " + this.labelWidth + 'px';
17355             }
17356            
17357             if(this.labelWidth < 13 && this.labelmd == 0){
17358                 this.labelmd = this.labelWidth;
17359             }
17360             
17361             if(this.labellg > 0){
17362                 labelCfg.cls += ' col-lg-' + this.labellg;
17363                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17364             }
17365             
17366             if(this.labelmd > 0){
17367                 labelCfg.cls += ' col-md-' + this.labelmd;
17368                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17369             }
17370             
17371             if(this.labelsm > 0){
17372                 labelCfg.cls += ' col-sm-' + this.labelsm;
17373                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17374             }
17375             
17376             if(this.labelxs > 0){
17377                 labelCfg.cls += ' col-xs-' + this.labelxs;
17378                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17379             }
17380                 
17381                 
17382         } else if ( this.fieldLabel.length) {
17383             cfg.cn = [
17384                 {
17385                    tag : 'i',
17386                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17387                    tooltip : 'This field is required'
17388                 },
17389                 {
17390                     tag: 'label',
17391                     cls : 'control-label',
17392                     html : this.fieldLabel
17393
17394                 },
17395                 {
17396                     cls : '', 
17397                     cn: [
17398                         combobox
17399                     ]
17400                 }
17401             ];
17402             
17403             if(this.indicatorpos == 'right'){
17404                 cfg.cn = [
17405                     {
17406                         tag: 'label',
17407                         cls : 'control-label',
17408                         html : this.fieldLabel,
17409                         cn : [
17410                             {
17411                                tag : 'i',
17412                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17413                                tooltip : 'This field is required'
17414                             }
17415                         ]
17416                     },
17417                     {
17418                         cls : '', 
17419                         cn: [
17420                             combobox
17421                         ]
17422                     }
17423                 ];
17424             }
17425         } else {
17426             cfg.cn = combobox;    
17427         }
17428         
17429         
17430         var settings = this;
17431         
17432         ['xs','sm','md','lg'].map(function(size){
17433             if (settings[size]) {
17434                 cfg.cls += ' col-' + size + '-' + settings[size];
17435             }
17436         });
17437         
17438         return cfg;
17439     },
17440     
17441     initTouchView : function()
17442     {
17443         this.renderTouchView();
17444         
17445         this.touchViewEl.on('scroll', function(){
17446             this.el.dom.scrollTop = 0;
17447         }, this);
17448         
17449         this.originalValue = this.getValue();
17450         
17451         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17452         
17453         this.inputEl().on("click", this.showTouchView, this);
17454         if (this.triggerEl) {
17455             this.triggerEl.on("click", this.showTouchView, this);
17456         }
17457         
17458         
17459         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17460         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17461         
17462         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17463         
17464         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17465         this.store.on('load', this.onTouchViewLoad, this);
17466         this.store.on('loadexception', this.onTouchViewLoadException, this);
17467         
17468         if(this.hiddenName){
17469             
17470             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17471             
17472             this.hiddenField.dom.value =
17473                 this.hiddenValue !== undefined ? this.hiddenValue :
17474                 this.value !== undefined ? this.value : '';
17475         
17476             this.el.dom.removeAttribute('name');
17477             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17478         }
17479         
17480         if(this.multiple){
17481             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17482             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17483         }
17484         
17485         if(this.removable && !this.multiple){
17486             var close = this.closeTriggerEl();
17487             if(close){
17488                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17489                 close.on('click', this.removeBtnClick, this, close);
17490             }
17491         }
17492         /*
17493          * fix the bug in Safari iOS8
17494          */
17495         this.inputEl().on("focus", function(e){
17496             document.activeElement.blur();
17497         }, this);
17498         
17499         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17500         
17501         return;
17502         
17503         
17504     },
17505     
17506     renderTouchView : function()
17507     {
17508         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17509         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17510         
17511         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17512         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17513         
17514         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17515         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17516         this.touchViewBodyEl.setStyle('overflow', 'auto');
17517         
17518         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17519         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17520         
17521         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17522         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17523         
17524     },
17525     
17526     showTouchView : function()
17527     {
17528         if(this.disabled){
17529             return;
17530         }
17531         
17532         this.touchViewHeaderEl.hide();
17533
17534         if(this.modalTitle.length){
17535             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17536             this.touchViewHeaderEl.show();
17537         }
17538
17539         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17540         this.touchViewEl.show();
17541
17542         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17543         
17544         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17545         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17546
17547         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17548
17549         if(this.modalTitle.length){
17550             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17551         }
17552         
17553         this.touchViewBodyEl.setHeight(bodyHeight);
17554
17555         if(this.animate){
17556             var _this = this;
17557             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17558         }else{
17559             this.touchViewEl.addClass(['in','show']);
17560         }
17561         
17562         if(this._touchViewMask){
17563             Roo.get(document.body).addClass("x-body-masked");
17564             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17565             this._touchViewMask.setStyle('z-index', 10000);
17566             this._touchViewMask.addClass('show');
17567         }
17568         
17569         this.doTouchViewQuery();
17570         
17571     },
17572     
17573     hideTouchView : function()
17574     {
17575         this.touchViewEl.removeClass(['in','show']);
17576
17577         if(this.animate){
17578             var _this = this;
17579             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17580         }else{
17581             this.touchViewEl.setStyle('display', 'none');
17582         }
17583         
17584         if(this._touchViewMask){
17585             this._touchViewMask.removeClass('show');
17586             Roo.get(document.body).removeClass("x-body-masked");
17587         }
17588     },
17589     
17590     setTouchViewValue : function()
17591     {
17592         if(this.multiple){
17593             this.clearItem();
17594         
17595             var _this = this;
17596
17597             Roo.each(this.tickItems, function(o){
17598                 this.addItem(o);
17599             }, this);
17600         }
17601         
17602         this.hideTouchView();
17603     },
17604     
17605     doTouchViewQuery : function()
17606     {
17607         var qe = {
17608             query: '',
17609             forceAll: true,
17610             combo: this,
17611             cancel:false
17612         };
17613         
17614         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17615             return false;
17616         }
17617         
17618         if(!this.alwaysQuery || this.mode == 'local'){
17619             this.onTouchViewLoad();
17620             return;
17621         }
17622         
17623         this.store.load();
17624     },
17625     
17626     onTouchViewBeforeLoad : function(combo,opts)
17627     {
17628         return;
17629     },
17630
17631     // private
17632     onTouchViewLoad : function()
17633     {
17634         if(this.store.getCount() < 1){
17635             this.onTouchViewEmptyResults();
17636             return;
17637         }
17638         
17639         this.clearTouchView();
17640         
17641         var rawValue = this.getRawValue();
17642         
17643         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17644         
17645         this.tickItems = [];
17646         
17647         this.store.data.each(function(d, rowIndex){
17648             var row = this.touchViewListGroup.createChild(template);
17649             
17650             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17651                 row.addClass(d.data.cls);
17652             }
17653             
17654             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17655                 var cfg = {
17656                     data : d.data,
17657                     html : d.data[this.displayField]
17658                 };
17659                 
17660                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17661                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17662                 }
17663             }
17664             row.removeClass('selected');
17665             if(!this.multiple && this.valueField &&
17666                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17667             {
17668                 // radio buttons..
17669                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17670                 row.addClass('selected');
17671             }
17672             
17673             if(this.multiple && this.valueField &&
17674                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17675             {
17676                 
17677                 // checkboxes...
17678                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17679                 this.tickItems.push(d.data);
17680             }
17681             
17682             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17683             
17684         }, this);
17685         
17686         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17687         
17688         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17689
17690         if(this.modalTitle.length){
17691             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17692         }
17693
17694         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17695         
17696         if(this.mobile_restrict_height && listHeight < bodyHeight){
17697             this.touchViewBodyEl.setHeight(listHeight);
17698         }
17699         
17700         var _this = this;
17701         
17702         if(firstChecked && listHeight > bodyHeight){
17703             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17704         }
17705         
17706     },
17707     
17708     onTouchViewLoadException : function()
17709     {
17710         this.hideTouchView();
17711     },
17712     
17713     onTouchViewEmptyResults : function()
17714     {
17715         this.clearTouchView();
17716         
17717         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17718         
17719         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17720         
17721     },
17722     
17723     clearTouchView : function()
17724     {
17725         this.touchViewListGroup.dom.innerHTML = '';
17726     },
17727     
17728     onTouchViewClick : function(e, el, o)
17729     {
17730         e.preventDefault();
17731         
17732         var row = o.row;
17733         var rowIndex = o.rowIndex;
17734         
17735         var r = this.store.getAt(rowIndex);
17736         
17737         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17738             
17739             if(!this.multiple){
17740                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17741                     c.dom.removeAttribute('checked');
17742                 }, this);
17743
17744                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17745
17746                 this.setFromData(r.data);
17747
17748                 var close = this.closeTriggerEl();
17749
17750                 if(close){
17751                     close.show();
17752                 }
17753
17754                 this.hideTouchView();
17755
17756                 this.fireEvent('select', this, r, rowIndex);
17757
17758                 return;
17759             }
17760
17761             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17762                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17763                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17764                 return;
17765             }
17766
17767             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17768             this.addItem(r.data);
17769             this.tickItems.push(r.data);
17770         }
17771     },
17772     
17773     getAutoCreateNativeIOS : function()
17774     {
17775         var cfg = {
17776             cls: 'form-group' //input-group,
17777         };
17778         
17779         var combobox =  {
17780             tag: 'select',
17781             cls : 'roo-ios-select'
17782         };
17783         
17784         if (this.name) {
17785             combobox.name = this.name;
17786         }
17787         
17788         if (this.disabled) {
17789             combobox.disabled = true;
17790         }
17791         
17792         var settings = this;
17793         
17794         ['xs','sm','md','lg'].map(function(size){
17795             if (settings[size]) {
17796                 cfg.cls += ' col-' + size + '-' + settings[size];
17797             }
17798         });
17799         
17800         cfg.cn = combobox;
17801         
17802         return cfg;
17803         
17804     },
17805     
17806     initIOSView : function()
17807     {
17808         this.store.on('load', this.onIOSViewLoad, this);
17809         
17810         return;
17811     },
17812     
17813     onIOSViewLoad : function()
17814     {
17815         if(this.store.getCount() < 1){
17816             return;
17817         }
17818         
17819         this.clearIOSView();
17820         
17821         if(this.allowBlank) {
17822             
17823             var default_text = '-- SELECT --';
17824             
17825             if(this.placeholder.length){
17826                 default_text = this.placeholder;
17827             }
17828             
17829             if(this.emptyTitle.length){
17830                 default_text += ' - ' + this.emptyTitle + ' -';
17831             }
17832             
17833             var opt = this.inputEl().createChild({
17834                 tag: 'option',
17835                 value : 0,
17836                 html : default_text
17837             });
17838             
17839             var o = {};
17840             o[this.valueField] = 0;
17841             o[this.displayField] = default_text;
17842             
17843             this.ios_options.push({
17844                 data : o,
17845                 el : opt
17846             });
17847             
17848         }
17849         
17850         this.store.data.each(function(d, rowIndex){
17851             
17852             var html = '';
17853             
17854             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17855                 html = d.data[this.displayField];
17856             }
17857             
17858             var value = '';
17859             
17860             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17861                 value = d.data[this.valueField];
17862             }
17863             
17864             var option = {
17865                 tag: 'option',
17866                 value : value,
17867                 html : html
17868             };
17869             
17870             if(this.value == d.data[this.valueField]){
17871                 option['selected'] = true;
17872             }
17873             
17874             var opt = this.inputEl().createChild(option);
17875             
17876             this.ios_options.push({
17877                 data : d.data,
17878                 el : opt
17879             });
17880             
17881         }, this);
17882         
17883         this.inputEl().on('change', function(){
17884            this.fireEvent('select', this);
17885         }, this);
17886         
17887     },
17888     
17889     clearIOSView: function()
17890     {
17891         this.inputEl().dom.innerHTML = '';
17892         
17893         this.ios_options = [];
17894     },
17895     
17896     setIOSValue: function(v)
17897     {
17898         this.value = v;
17899         
17900         if(!this.ios_options){
17901             return;
17902         }
17903         
17904         Roo.each(this.ios_options, function(opts){
17905            
17906            opts.el.dom.removeAttribute('selected');
17907            
17908            if(opts.data[this.valueField] != v){
17909                return;
17910            }
17911            
17912            opts.el.dom.setAttribute('selected', true);
17913            
17914         }, this);
17915     }
17916
17917     /** 
17918     * @cfg {Boolean} grow 
17919     * @hide 
17920     */
17921     /** 
17922     * @cfg {Number} growMin 
17923     * @hide 
17924     */
17925     /** 
17926     * @cfg {Number} growMax 
17927     * @hide 
17928     */
17929     /**
17930      * @hide
17931      * @method autoSize
17932      */
17933 });
17934
17935 Roo.apply(Roo.bootstrap.ComboBox,  {
17936     
17937     header : {
17938         tag: 'div',
17939         cls: 'modal-header',
17940         cn: [
17941             {
17942                 tag: 'h4',
17943                 cls: 'modal-title'
17944             }
17945         ]
17946     },
17947     
17948     body : {
17949         tag: 'div',
17950         cls: 'modal-body',
17951         cn: [
17952             {
17953                 tag: 'ul',
17954                 cls: 'list-group'
17955             }
17956         ]
17957     },
17958     
17959     listItemRadio : {
17960         tag: 'li',
17961         cls: 'list-group-item',
17962         cn: [
17963             {
17964                 tag: 'span',
17965                 cls: 'roo-combobox-list-group-item-value'
17966             },
17967             {
17968                 tag: 'div',
17969                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17970                 cn: [
17971                     {
17972                         tag: 'input',
17973                         type: 'radio'
17974                     },
17975                     {
17976                         tag: 'label'
17977                     }
17978                 ]
17979             }
17980         ]
17981     },
17982     
17983     listItemCheckbox : {
17984         tag: 'li',
17985         cls: 'list-group-item',
17986         cn: [
17987             {
17988                 tag: 'span',
17989                 cls: 'roo-combobox-list-group-item-value'
17990             },
17991             {
17992                 tag: 'div',
17993                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17994                 cn: [
17995                     {
17996                         tag: 'input',
17997                         type: 'checkbox'
17998                     },
17999                     {
18000                         tag: 'label'
18001                     }
18002                 ]
18003             }
18004         ]
18005     },
18006     
18007     emptyResult : {
18008         tag: 'div',
18009         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18010     },
18011     
18012     footer : {
18013         tag: 'div',
18014         cls: 'modal-footer',
18015         cn: [
18016             {
18017                 tag: 'div',
18018                 cls: 'row',
18019                 cn: [
18020                     {
18021                         tag: 'div',
18022                         cls: 'col-xs-6 text-left',
18023                         cn: {
18024                             tag: 'button',
18025                             cls: 'btn btn-danger roo-touch-view-cancel',
18026                             html: 'Cancel'
18027                         }
18028                     },
18029                     {
18030                         tag: 'div',
18031                         cls: 'col-xs-6 text-right',
18032                         cn: {
18033                             tag: 'button',
18034                             cls: 'btn btn-success roo-touch-view-ok',
18035                             html: 'OK'
18036                         }
18037                     }
18038                 ]
18039             }
18040         ]
18041         
18042     }
18043 });
18044
18045 Roo.apply(Roo.bootstrap.ComboBox,  {
18046     
18047     touchViewTemplate : {
18048         tag: 'div',
18049         cls: 'modal fade roo-combobox-touch-view',
18050         cn: [
18051             {
18052                 tag: 'div',
18053                 cls: 'modal-dialog',
18054                 style : 'position:fixed', // we have to fix position....
18055                 cn: [
18056                     {
18057                         tag: 'div',
18058                         cls: 'modal-content',
18059                         cn: [
18060                             Roo.bootstrap.ComboBox.header,
18061                             Roo.bootstrap.ComboBox.body,
18062                             Roo.bootstrap.ComboBox.footer
18063                         ]
18064                     }
18065                 ]
18066             }
18067         ]
18068     }
18069 });/*
18070  * Based on:
18071  * Ext JS Library 1.1.1
18072  * Copyright(c) 2006-2007, Ext JS, LLC.
18073  *
18074  * Originally Released Under LGPL - original licence link has changed is not relivant.
18075  *
18076  * Fork - LGPL
18077  * <script type="text/javascript">
18078  */
18079
18080 /**
18081  * @class Roo.View
18082  * @extends Roo.util.Observable
18083  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18084  * This class also supports single and multi selection modes. <br>
18085  * Create a data model bound view:
18086  <pre><code>
18087  var store = new Roo.data.Store(...);
18088
18089  var view = new Roo.View({
18090     el : "my-element",
18091     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18092  
18093     singleSelect: true,
18094     selectedClass: "ydataview-selected",
18095     store: store
18096  });
18097
18098  // listen for node click?
18099  view.on("click", function(vw, index, node, e){
18100  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18101  });
18102
18103  // load XML data
18104  dataModel.load("foobar.xml");
18105  </code></pre>
18106  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18107  * <br><br>
18108  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18109  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18110  * 
18111  * Note: old style constructor is still suported (container, template, config)
18112  * 
18113  * @constructor
18114  * Create a new View
18115  * @param {Object} config The config object
18116  * 
18117  */
18118 Roo.View = function(config, depreciated_tpl, depreciated_config){
18119     
18120     this.parent = false;
18121     
18122     if (typeof(depreciated_tpl) == 'undefined') {
18123         // new way.. - universal constructor.
18124         Roo.apply(this, config);
18125         this.el  = Roo.get(this.el);
18126     } else {
18127         // old format..
18128         this.el  = Roo.get(config);
18129         this.tpl = depreciated_tpl;
18130         Roo.apply(this, depreciated_config);
18131     }
18132     this.wrapEl  = this.el.wrap().wrap();
18133     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18134     
18135     
18136     if(typeof(this.tpl) == "string"){
18137         this.tpl = new Roo.Template(this.tpl);
18138     } else {
18139         // support xtype ctors..
18140         this.tpl = new Roo.factory(this.tpl, Roo);
18141     }
18142     
18143     
18144     this.tpl.compile();
18145     
18146     /** @private */
18147     this.addEvents({
18148         /**
18149          * @event beforeclick
18150          * Fires before a click is processed. Returns false to cancel the default action.
18151          * @param {Roo.View} this
18152          * @param {Number} index The index of the target node
18153          * @param {HTMLElement} node The target node
18154          * @param {Roo.EventObject} e The raw event object
18155          */
18156             "beforeclick" : true,
18157         /**
18158          * @event click
18159          * Fires when a template node is clicked.
18160          * @param {Roo.View} this
18161          * @param {Number} index The index of the target node
18162          * @param {HTMLElement} node The target node
18163          * @param {Roo.EventObject} e The raw event object
18164          */
18165             "click" : true,
18166         /**
18167          * @event dblclick
18168          * Fires when a template node is double clicked.
18169          * @param {Roo.View} this
18170          * @param {Number} index The index of the target node
18171          * @param {HTMLElement} node The target node
18172          * @param {Roo.EventObject} e The raw event object
18173          */
18174             "dblclick" : true,
18175         /**
18176          * @event contextmenu
18177          * Fires when a template node is right clicked.
18178          * @param {Roo.View} this
18179          * @param {Number} index The index of the target node
18180          * @param {HTMLElement} node The target node
18181          * @param {Roo.EventObject} e The raw event object
18182          */
18183             "contextmenu" : true,
18184         /**
18185          * @event selectionchange
18186          * Fires when the selected nodes change.
18187          * @param {Roo.View} this
18188          * @param {Array} selections Array of the selected nodes
18189          */
18190             "selectionchange" : true,
18191     
18192         /**
18193          * @event beforeselect
18194          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18195          * @param {Roo.View} this
18196          * @param {HTMLElement} node The node to be selected
18197          * @param {Array} selections Array of currently selected nodes
18198          */
18199             "beforeselect" : true,
18200         /**
18201          * @event preparedata
18202          * Fires on every row to render, to allow you to change the data.
18203          * @param {Roo.View} this
18204          * @param {Object} data to be rendered (change this)
18205          */
18206           "preparedata" : true
18207           
18208           
18209         });
18210
18211
18212
18213     this.el.on({
18214         "click": this.onClick,
18215         "dblclick": this.onDblClick,
18216         "contextmenu": this.onContextMenu,
18217         scope:this
18218     });
18219
18220     this.selections = [];
18221     this.nodes = [];
18222     this.cmp = new Roo.CompositeElementLite([]);
18223     if(this.store){
18224         this.store = Roo.factory(this.store, Roo.data);
18225         this.setStore(this.store, true);
18226     }
18227     
18228     if ( this.footer && this.footer.xtype) {
18229            
18230          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18231         
18232         this.footer.dataSource = this.store;
18233         this.footer.container = fctr;
18234         this.footer = Roo.factory(this.footer, Roo);
18235         fctr.insertFirst(this.el);
18236         
18237         // this is a bit insane - as the paging toolbar seems to detach the el..
18238 //        dom.parentNode.parentNode.parentNode
18239          // they get detached?
18240     }
18241     
18242     
18243     Roo.View.superclass.constructor.call(this);
18244     
18245     
18246 };
18247
18248 Roo.extend(Roo.View, Roo.util.Observable, {
18249     
18250      /**
18251      * @cfg {Roo.data.Store} store Data store to load data from.
18252      */
18253     store : false,
18254     
18255     /**
18256      * @cfg {String|Roo.Element} el The container element.
18257      */
18258     el : '',
18259     
18260     /**
18261      * @cfg {String|Roo.Template} tpl The template used by this View 
18262      */
18263     tpl : false,
18264     /**
18265      * @cfg {String} dataName the named area of the template to use as the data area
18266      *                          Works with domtemplates roo-name="name"
18267      */
18268     dataName: false,
18269     /**
18270      * @cfg {String} selectedClass The css class to add to selected nodes
18271      */
18272     selectedClass : "x-view-selected",
18273      /**
18274      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18275      */
18276     emptyText : "",
18277     
18278     /**
18279      * @cfg {String} text to display on mask (default Loading)
18280      */
18281     mask : false,
18282     /**
18283      * @cfg {Boolean} multiSelect Allow multiple selection
18284      */
18285     multiSelect : false,
18286     /**
18287      * @cfg {Boolean} singleSelect Allow single selection
18288      */
18289     singleSelect:  false,
18290     
18291     /**
18292      * @cfg {Boolean} toggleSelect - selecting 
18293      */
18294     toggleSelect : false,
18295     
18296     /**
18297      * @cfg {Boolean} tickable - selecting 
18298      */
18299     tickable : false,
18300     
18301     /**
18302      * Returns the element this view is bound to.
18303      * @return {Roo.Element}
18304      */
18305     getEl : function(){
18306         return this.wrapEl;
18307     },
18308     
18309     
18310
18311     /**
18312      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18313      */
18314     refresh : function(){
18315         //Roo.log('refresh');
18316         var t = this.tpl;
18317         
18318         // if we are using something like 'domtemplate', then
18319         // the what gets used is:
18320         // t.applySubtemplate(NAME, data, wrapping data..)
18321         // the outer template then get' applied with
18322         //     the store 'extra data'
18323         // and the body get's added to the
18324         //      roo-name="data" node?
18325         //      <span class='roo-tpl-{name}'></span> ?????
18326         
18327         
18328         
18329         this.clearSelections();
18330         this.el.update("");
18331         var html = [];
18332         var records = this.store.getRange();
18333         if(records.length < 1) {
18334             
18335             // is this valid??  = should it render a template??
18336             
18337             this.el.update(this.emptyText);
18338             return;
18339         }
18340         var el = this.el;
18341         if (this.dataName) {
18342             this.el.update(t.apply(this.store.meta)); //????
18343             el = this.el.child('.roo-tpl-' + this.dataName);
18344         }
18345         
18346         for(var i = 0, len = records.length; i < len; i++){
18347             var data = this.prepareData(records[i].data, i, records[i]);
18348             this.fireEvent("preparedata", this, data, i, records[i]);
18349             
18350             var d = Roo.apply({}, data);
18351             
18352             if(this.tickable){
18353                 Roo.apply(d, {'roo-id' : Roo.id()});
18354                 
18355                 var _this = this;
18356             
18357                 Roo.each(this.parent.item, function(item){
18358                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18359                         return;
18360                     }
18361                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18362                 });
18363             }
18364             
18365             html[html.length] = Roo.util.Format.trim(
18366                 this.dataName ?
18367                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18368                     t.apply(d)
18369             );
18370         }
18371         
18372         
18373         
18374         el.update(html.join(""));
18375         this.nodes = el.dom.childNodes;
18376         this.updateIndexes(0);
18377     },
18378     
18379
18380     /**
18381      * Function to override to reformat the data that is sent to
18382      * the template for each node.
18383      * DEPRICATED - use the preparedata event handler.
18384      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18385      * a JSON object for an UpdateManager bound view).
18386      */
18387     prepareData : function(data, index, record)
18388     {
18389         this.fireEvent("preparedata", this, data, index, record);
18390         return data;
18391     },
18392
18393     onUpdate : function(ds, record){
18394         // Roo.log('on update');   
18395         this.clearSelections();
18396         var index = this.store.indexOf(record);
18397         var n = this.nodes[index];
18398         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18399         n.parentNode.removeChild(n);
18400         this.updateIndexes(index, index);
18401     },
18402
18403     
18404     
18405 // --------- FIXME     
18406     onAdd : function(ds, records, index)
18407     {
18408         //Roo.log(['on Add', ds, records, index] );        
18409         this.clearSelections();
18410         if(this.nodes.length == 0){
18411             this.refresh();
18412             return;
18413         }
18414         var n = this.nodes[index];
18415         for(var i = 0, len = records.length; i < len; i++){
18416             var d = this.prepareData(records[i].data, i, records[i]);
18417             if(n){
18418                 this.tpl.insertBefore(n, d);
18419             }else{
18420                 
18421                 this.tpl.append(this.el, d);
18422             }
18423         }
18424         this.updateIndexes(index);
18425     },
18426
18427     onRemove : function(ds, record, index){
18428        // Roo.log('onRemove');
18429         this.clearSelections();
18430         var el = this.dataName  ?
18431             this.el.child('.roo-tpl-' + this.dataName) :
18432             this.el; 
18433         
18434         el.dom.removeChild(this.nodes[index]);
18435         this.updateIndexes(index);
18436     },
18437
18438     /**
18439      * Refresh an individual node.
18440      * @param {Number} index
18441      */
18442     refreshNode : function(index){
18443         this.onUpdate(this.store, this.store.getAt(index));
18444     },
18445
18446     updateIndexes : function(startIndex, endIndex){
18447         var ns = this.nodes;
18448         startIndex = startIndex || 0;
18449         endIndex = endIndex || ns.length - 1;
18450         for(var i = startIndex; i <= endIndex; i++){
18451             ns[i].nodeIndex = i;
18452         }
18453     },
18454
18455     /**
18456      * Changes the data store this view uses and refresh the view.
18457      * @param {Store} store
18458      */
18459     setStore : function(store, initial){
18460         if(!initial && this.store){
18461             this.store.un("datachanged", this.refresh);
18462             this.store.un("add", this.onAdd);
18463             this.store.un("remove", this.onRemove);
18464             this.store.un("update", this.onUpdate);
18465             this.store.un("clear", this.refresh);
18466             this.store.un("beforeload", this.onBeforeLoad);
18467             this.store.un("load", this.onLoad);
18468             this.store.un("loadexception", this.onLoad);
18469         }
18470         if(store){
18471           
18472             store.on("datachanged", this.refresh, this);
18473             store.on("add", this.onAdd, this);
18474             store.on("remove", this.onRemove, this);
18475             store.on("update", this.onUpdate, this);
18476             store.on("clear", this.refresh, this);
18477             store.on("beforeload", this.onBeforeLoad, this);
18478             store.on("load", this.onLoad, this);
18479             store.on("loadexception", this.onLoad, this);
18480         }
18481         
18482         if(store){
18483             this.refresh();
18484         }
18485     },
18486     /**
18487      * onbeforeLoad - masks the loading area.
18488      *
18489      */
18490     onBeforeLoad : function(store,opts)
18491     {
18492          //Roo.log('onBeforeLoad');   
18493         if (!opts.add) {
18494             this.el.update("");
18495         }
18496         this.el.mask(this.mask ? this.mask : "Loading" ); 
18497     },
18498     onLoad : function ()
18499     {
18500         this.el.unmask();
18501     },
18502     
18503
18504     /**
18505      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18506      * @param {HTMLElement} node
18507      * @return {HTMLElement} The template node
18508      */
18509     findItemFromChild : function(node){
18510         var el = this.dataName  ?
18511             this.el.child('.roo-tpl-' + this.dataName,true) :
18512             this.el.dom; 
18513         
18514         if(!node || node.parentNode == el){
18515                     return node;
18516             }
18517             var p = node.parentNode;
18518             while(p && p != el){
18519             if(p.parentNode == el){
18520                 return p;
18521             }
18522             p = p.parentNode;
18523         }
18524             return null;
18525     },
18526
18527     /** @ignore */
18528     onClick : function(e){
18529         var item = this.findItemFromChild(e.getTarget());
18530         if(item){
18531             var index = this.indexOf(item);
18532             if(this.onItemClick(item, index, e) !== false){
18533                 this.fireEvent("click", this, index, item, e);
18534             }
18535         }else{
18536             this.clearSelections();
18537         }
18538     },
18539
18540     /** @ignore */
18541     onContextMenu : function(e){
18542         var item = this.findItemFromChild(e.getTarget());
18543         if(item){
18544             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18545         }
18546     },
18547
18548     /** @ignore */
18549     onDblClick : function(e){
18550         var item = this.findItemFromChild(e.getTarget());
18551         if(item){
18552             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18553         }
18554     },
18555
18556     onItemClick : function(item, index, e)
18557     {
18558         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18559             return false;
18560         }
18561         if (this.toggleSelect) {
18562             var m = this.isSelected(item) ? 'unselect' : 'select';
18563             //Roo.log(m);
18564             var _t = this;
18565             _t[m](item, true, false);
18566             return true;
18567         }
18568         if(this.multiSelect || this.singleSelect){
18569             if(this.multiSelect && e.shiftKey && this.lastSelection){
18570                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18571             }else{
18572                 this.select(item, this.multiSelect && e.ctrlKey);
18573                 this.lastSelection = item;
18574             }
18575             
18576             if(!this.tickable){
18577                 e.preventDefault();
18578             }
18579             
18580         }
18581         return true;
18582     },
18583
18584     /**
18585      * Get the number of selected nodes.
18586      * @return {Number}
18587      */
18588     getSelectionCount : function(){
18589         return this.selections.length;
18590     },
18591
18592     /**
18593      * Get the currently selected nodes.
18594      * @return {Array} An array of HTMLElements
18595      */
18596     getSelectedNodes : function(){
18597         return this.selections;
18598     },
18599
18600     /**
18601      * Get the indexes of the selected nodes.
18602      * @return {Array}
18603      */
18604     getSelectedIndexes : function(){
18605         var indexes = [], s = this.selections;
18606         for(var i = 0, len = s.length; i < len; i++){
18607             indexes.push(s[i].nodeIndex);
18608         }
18609         return indexes;
18610     },
18611
18612     /**
18613      * Clear all selections
18614      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18615      */
18616     clearSelections : function(suppressEvent){
18617         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18618             this.cmp.elements = this.selections;
18619             this.cmp.removeClass(this.selectedClass);
18620             this.selections = [];
18621             if(!suppressEvent){
18622                 this.fireEvent("selectionchange", this, this.selections);
18623             }
18624         }
18625     },
18626
18627     /**
18628      * Returns true if the passed node is selected
18629      * @param {HTMLElement/Number} node The node or node index
18630      * @return {Boolean}
18631      */
18632     isSelected : function(node){
18633         var s = this.selections;
18634         if(s.length < 1){
18635             return false;
18636         }
18637         node = this.getNode(node);
18638         return s.indexOf(node) !== -1;
18639     },
18640
18641     /**
18642      * Selects nodes.
18643      * @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
18644      * @param {Boolean} keepExisting (optional) true to keep existing selections
18645      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18646      */
18647     select : function(nodeInfo, keepExisting, suppressEvent){
18648         if(nodeInfo instanceof Array){
18649             if(!keepExisting){
18650                 this.clearSelections(true);
18651             }
18652             for(var i = 0, len = nodeInfo.length; i < len; i++){
18653                 this.select(nodeInfo[i], true, true);
18654             }
18655             return;
18656         } 
18657         var node = this.getNode(nodeInfo);
18658         if(!node || this.isSelected(node)){
18659             return; // already selected.
18660         }
18661         if(!keepExisting){
18662             this.clearSelections(true);
18663         }
18664         
18665         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18666             Roo.fly(node).addClass(this.selectedClass);
18667             this.selections.push(node);
18668             if(!suppressEvent){
18669                 this.fireEvent("selectionchange", this, this.selections);
18670             }
18671         }
18672         
18673         
18674     },
18675       /**
18676      * Unselects nodes.
18677      * @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
18678      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18679      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18680      */
18681     unselect : function(nodeInfo, keepExisting, suppressEvent)
18682     {
18683         if(nodeInfo instanceof Array){
18684             Roo.each(this.selections, function(s) {
18685                 this.unselect(s, nodeInfo);
18686             }, this);
18687             return;
18688         }
18689         var node = this.getNode(nodeInfo);
18690         if(!node || !this.isSelected(node)){
18691             //Roo.log("not selected");
18692             return; // not selected.
18693         }
18694         // fireevent???
18695         var ns = [];
18696         Roo.each(this.selections, function(s) {
18697             if (s == node ) {
18698                 Roo.fly(node).removeClass(this.selectedClass);
18699
18700                 return;
18701             }
18702             ns.push(s);
18703         },this);
18704         
18705         this.selections= ns;
18706         this.fireEvent("selectionchange", this, this.selections);
18707     },
18708
18709     /**
18710      * Gets a template node.
18711      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18712      * @return {HTMLElement} The node or null if it wasn't found
18713      */
18714     getNode : function(nodeInfo){
18715         if(typeof nodeInfo == "string"){
18716             return document.getElementById(nodeInfo);
18717         }else if(typeof nodeInfo == "number"){
18718             return this.nodes[nodeInfo];
18719         }
18720         return nodeInfo;
18721     },
18722
18723     /**
18724      * Gets a range template nodes.
18725      * @param {Number} startIndex
18726      * @param {Number} endIndex
18727      * @return {Array} An array of nodes
18728      */
18729     getNodes : function(start, end){
18730         var ns = this.nodes;
18731         start = start || 0;
18732         end = typeof end == "undefined" ? ns.length - 1 : end;
18733         var nodes = [];
18734         if(start <= end){
18735             for(var i = start; i <= end; i++){
18736                 nodes.push(ns[i]);
18737             }
18738         } else{
18739             for(var i = start; i >= end; i--){
18740                 nodes.push(ns[i]);
18741             }
18742         }
18743         return nodes;
18744     },
18745
18746     /**
18747      * Finds the index of the passed node
18748      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18749      * @return {Number} The index of the node or -1
18750      */
18751     indexOf : function(node){
18752         node = this.getNode(node);
18753         if(typeof node.nodeIndex == "number"){
18754             return node.nodeIndex;
18755         }
18756         var ns = this.nodes;
18757         for(var i = 0, len = ns.length; i < len; i++){
18758             if(ns[i] == node){
18759                 return i;
18760             }
18761         }
18762         return -1;
18763     }
18764 });
18765 /*
18766  * - LGPL
18767  *
18768  * based on jquery fullcalendar
18769  * 
18770  */
18771
18772 Roo.bootstrap = Roo.bootstrap || {};
18773 /**
18774  * @class Roo.bootstrap.Calendar
18775  * @extends Roo.bootstrap.Component
18776  * Bootstrap Calendar class
18777  * @cfg {Boolean} loadMask (true|false) default false
18778  * @cfg {Object} header generate the user specific header of the calendar, default false
18779
18780  * @constructor
18781  * Create a new Container
18782  * @param {Object} config The config object
18783  */
18784
18785
18786
18787 Roo.bootstrap.Calendar = function(config){
18788     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18789      this.addEvents({
18790         /**
18791              * @event select
18792              * Fires when a date is selected
18793              * @param {DatePicker} this
18794              * @param {Date} date The selected date
18795              */
18796         'select': true,
18797         /**
18798              * @event monthchange
18799              * Fires when the displayed month changes 
18800              * @param {DatePicker} this
18801              * @param {Date} date The selected month
18802              */
18803         'monthchange': true,
18804         /**
18805              * @event evententer
18806              * Fires when mouse over an event
18807              * @param {Calendar} this
18808              * @param {event} Event
18809              */
18810         'evententer': true,
18811         /**
18812              * @event eventleave
18813              * Fires when the mouse leaves an
18814              * @param {Calendar} this
18815              * @param {event}
18816              */
18817         'eventleave': true,
18818         /**
18819              * @event eventclick
18820              * Fires when the mouse click an
18821              * @param {Calendar} this
18822              * @param {event}
18823              */
18824         'eventclick': true
18825         
18826     });
18827
18828 };
18829
18830 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18831     
18832      /**
18833      * @cfg {Number} startDay
18834      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18835      */
18836     startDay : 0,
18837     
18838     loadMask : false,
18839     
18840     header : false,
18841       
18842     getAutoCreate : function(){
18843         
18844         
18845         var fc_button = function(name, corner, style, content ) {
18846             return Roo.apply({},{
18847                 tag : 'span',
18848                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18849                          (corner.length ?
18850                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18851                             ''
18852                         ),
18853                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18854                 unselectable: 'on'
18855             });
18856         };
18857         
18858         var header = {};
18859         
18860         if(!this.header){
18861             header = {
18862                 tag : 'table',
18863                 cls : 'fc-header',
18864                 style : 'width:100%',
18865                 cn : [
18866                     {
18867                         tag: 'tr',
18868                         cn : [
18869                             {
18870                                 tag : 'td',
18871                                 cls : 'fc-header-left',
18872                                 cn : [
18873                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18874                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18875                                     { tag: 'span', cls: 'fc-header-space' },
18876                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18877
18878
18879                                 ]
18880                             },
18881
18882                             {
18883                                 tag : 'td',
18884                                 cls : 'fc-header-center',
18885                                 cn : [
18886                                     {
18887                                         tag: 'span',
18888                                         cls: 'fc-header-title',
18889                                         cn : {
18890                                             tag: 'H2',
18891                                             html : 'month / year'
18892                                         }
18893                                     }
18894
18895                                 ]
18896                             },
18897                             {
18898                                 tag : 'td',
18899                                 cls : 'fc-header-right',
18900                                 cn : [
18901                               /*      fc_button('month', 'left', '', 'month' ),
18902                                     fc_button('week', '', '', 'week' ),
18903                                     fc_button('day', 'right', '', 'day' )
18904                                 */    
18905
18906                                 ]
18907                             }
18908
18909                         ]
18910                     }
18911                 ]
18912             };
18913         }
18914         
18915         header = this.header;
18916         
18917        
18918         var cal_heads = function() {
18919             var ret = [];
18920             // fixme - handle this.
18921             
18922             for (var i =0; i < Date.dayNames.length; i++) {
18923                 var d = Date.dayNames[i];
18924                 ret.push({
18925                     tag: 'th',
18926                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18927                     html : d.substring(0,3)
18928                 });
18929                 
18930             }
18931             ret[0].cls += ' fc-first';
18932             ret[6].cls += ' fc-last';
18933             return ret;
18934         };
18935         var cal_cell = function(n) {
18936             return  {
18937                 tag: 'td',
18938                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18939                 cn : [
18940                     {
18941                         cn : [
18942                             {
18943                                 cls: 'fc-day-number',
18944                                 html: 'D'
18945                             },
18946                             {
18947                                 cls: 'fc-day-content',
18948                              
18949                                 cn : [
18950                                      {
18951                                         style: 'position: relative;' // height: 17px;
18952                                     }
18953                                 ]
18954                             }
18955                             
18956                             
18957                         ]
18958                     }
18959                 ]
18960                 
18961             }
18962         };
18963         var cal_rows = function() {
18964             
18965             var ret = [];
18966             for (var r = 0; r < 6; r++) {
18967                 var row= {
18968                     tag : 'tr',
18969                     cls : 'fc-week',
18970                     cn : []
18971                 };
18972                 
18973                 for (var i =0; i < Date.dayNames.length; i++) {
18974                     var d = Date.dayNames[i];
18975                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18976
18977                 }
18978                 row.cn[0].cls+=' fc-first';
18979                 row.cn[0].cn[0].style = 'min-height:90px';
18980                 row.cn[6].cls+=' fc-last';
18981                 ret.push(row);
18982                 
18983             }
18984             ret[0].cls += ' fc-first';
18985             ret[4].cls += ' fc-prev-last';
18986             ret[5].cls += ' fc-last';
18987             return ret;
18988             
18989         };
18990         
18991         var cal_table = {
18992             tag: 'table',
18993             cls: 'fc-border-separate',
18994             style : 'width:100%',
18995             cellspacing  : 0,
18996             cn : [
18997                 { 
18998                     tag: 'thead',
18999                     cn : [
19000                         { 
19001                             tag: 'tr',
19002                             cls : 'fc-first fc-last',
19003                             cn : cal_heads()
19004                         }
19005                     ]
19006                 },
19007                 { 
19008                     tag: 'tbody',
19009                     cn : cal_rows()
19010                 }
19011                   
19012             ]
19013         };
19014          
19015          var cfg = {
19016             cls : 'fc fc-ltr',
19017             cn : [
19018                 header,
19019                 {
19020                     cls : 'fc-content',
19021                     style : "position: relative;",
19022                     cn : [
19023                         {
19024                             cls : 'fc-view fc-view-month fc-grid',
19025                             style : 'position: relative',
19026                             unselectable : 'on',
19027                             cn : [
19028                                 {
19029                                     cls : 'fc-event-container',
19030                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19031                                 },
19032                                 cal_table
19033                             ]
19034                         }
19035                     ]
19036     
19037                 }
19038            ] 
19039             
19040         };
19041         
19042          
19043         
19044         return cfg;
19045     },
19046     
19047     
19048     initEvents : function()
19049     {
19050         if(!this.store){
19051             throw "can not find store for calendar";
19052         }
19053         
19054         var mark = {
19055             tag: "div",
19056             cls:"x-dlg-mask",
19057             style: "text-align:center",
19058             cn: [
19059                 {
19060                     tag: "div",
19061                     style: "background-color:white;width:50%;margin:250 auto",
19062                     cn: [
19063                         {
19064                             tag: "img",
19065                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19066                         },
19067                         {
19068                             tag: "span",
19069                             html: "Loading"
19070                         }
19071                         
19072                     ]
19073                 }
19074             ]
19075         };
19076         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19077         
19078         var size = this.el.select('.fc-content', true).first().getSize();
19079         this.maskEl.setSize(size.width, size.height);
19080         this.maskEl.enableDisplayMode("block");
19081         if(!this.loadMask){
19082             this.maskEl.hide();
19083         }
19084         
19085         this.store = Roo.factory(this.store, Roo.data);
19086         this.store.on('load', this.onLoad, this);
19087         this.store.on('beforeload', this.onBeforeLoad, this);
19088         
19089         this.resize();
19090         
19091         this.cells = this.el.select('.fc-day',true);
19092         //Roo.log(this.cells);
19093         this.textNodes = this.el.query('.fc-day-number');
19094         this.cells.addClassOnOver('fc-state-hover');
19095         
19096         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19097         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19098         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19099         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19100         
19101         this.on('monthchange', this.onMonthChange, this);
19102         
19103         this.update(new Date().clearTime());
19104     },
19105     
19106     resize : function() {
19107         var sz  = this.el.getSize();
19108         
19109         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19110         this.el.select('.fc-day-content div',true).setHeight(34);
19111     },
19112     
19113     
19114     // private
19115     showPrevMonth : function(e){
19116         this.update(this.activeDate.add("mo", -1));
19117     },
19118     showToday : function(e){
19119         this.update(new Date().clearTime());
19120     },
19121     // private
19122     showNextMonth : function(e){
19123         this.update(this.activeDate.add("mo", 1));
19124     },
19125
19126     // private
19127     showPrevYear : function(){
19128         this.update(this.activeDate.add("y", -1));
19129     },
19130
19131     // private
19132     showNextYear : function(){
19133         this.update(this.activeDate.add("y", 1));
19134     },
19135
19136     
19137    // private
19138     update : function(date)
19139     {
19140         var vd = this.activeDate;
19141         this.activeDate = date;
19142 //        if(vd && this.el){
19143 //            var t = date.getTime();
19144 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19145 //                Roo.log('using add remove');
19146 //                
19147 //                this.fireEvent('monthchange', this, date);
19148 //                
19149 //                this.cells.removeClass("fc-state-highlight");
19150 //                this.cells.each(function(c){
19151 //                   if(c.dateValue == t){
19152 //                       c.addClass("fc-state-highlight");
19153 //                       setTimeout(function(){
19154 //                            try{c.dom.firstChild.focus();}catch(e){}
19155 //                       }, 50);
19156 //                       return false;
19157 //                   }
19158 //                   return true;
19159 //                });
19160 //                return;
19161 //            }
19162 //        }
19163         
19164         var days = date.getDaysInMonth();
19165         
19166         var firstOfMonth = date.getFirstDateOfMonth();
19167         var startingPos = firstOfMonth.getDay()-this.startDay;
19168         
19169         if(startingPos < this.startDay){
19170             startingPos += 7;
19171         }
19172         
19173         var pm = date.add(Date.MONTH, -1);
19174         var prevStart = pm.getDaysInMonth()-startingPos;
19175 //        
19176         this.cells = this.el.select('.fc-day',true);
19177         this.textNodes = this.el.query('.fc-day-number');
19178         this.cells.addClassOnOver('fc-state-hover');
19179         
19180         var cells = this.cells.elements;
19181         var textEls = this.textNodes;
19182         
19183         Roo.each(cells, function(cell){
19184             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19185         });
19186         
19187         days += startingPos;
19188
19189         // convert everything to numbers so it's fast
19190         var day = 86400000;
19191         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19192         //Roo.log(d);
19193         //Roo.log(pm);
19194         //Roo.log(prevStart);
19195         
19196         var today = new Date().clearTime().getTime();
19197         var sel = date.clearTime().getTime();
19198         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19199         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19200         var ddMatch = this.disabledDatesRE;
19201         var ddText = this.disabledDatesText;
19202         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19203         var ddaysText = this.disabledDaysText;
19204         var format = this.format;
19205         
19206         var setCellClass = function(cal, cell){
19207             cell.row = 0;
19208             cell.events = [];
19209             cell.more = [];
19210             //Roo.log('set Cell Class');
19211             cell.title = "";
19212             var t = d.getTime();
19213             
19214             //Roo.log(d);
19215             
19216             cell.dateValue = t;
19217             if(t == today){
19218                 cell.className += " fc-today";
19219                 cell.className += " fc-state-highlight";
19220                 cell.title = cal.todayText;
19221             }
19222             if(t == sel){
19223                 // disable highlight in other month..
19224                 //cell.className += " fc-state-highlight";
19225                 
19226             }
19227             // disabling
19228             if(t < min) {
19229                 cell.className = " fc-state-disabled";
19230                 cell.title = cal.minText;
19231                 return;
19232             }
19233             if(t > max) {
19234                 cell.className = " fc-state-disabled";
19235                 cell.title = cal.maxText;
19236                 return;
19237             }
19238             if(ddays){
19239                 if(ddays.indexOf(d.getDay()) != -1){
19240                     cell.title = ddaysText;
19241                     cell.className = " fc-state-disabled";
19242                 }
19243             }
19244             if(ddMatch && format){
19245                 var fvalue = d.dateFormat(format);
19246                 if(ddMatch.test(fvalue)){
19247                     cell.title = ddText.replace("%0", fvalue);
19248                     cell.className = " fc-state-disabled";
19249                 }
19250             }
19251             
19252             if (!cell.initialClassName) {
19253                 cell.initialClassName = cell.dom.className;
19254             }
19255             
19256             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19257         };
19258
19259         var i = 0;
19260         
19261         for(; i < startingPos; i++) {
19262             textEls[i].innerHTML = (++prevStart);
19263             d.setDate(d.getDate()+1);
19264             
19265             cells[i].className = "fc-past fc-other-month";
19266             setCellClass(this, cells[i]);
19267         }
19268         
19269         var intDay = 0;
19270         
19271         for(; i < days; i++){
19272             intDay = i - startingPos + 1;
19273             textEls[i].innerHTML = (intDay);
19274             d.setDate(d.getDate()+1);
19275             
19276             cells[i].className = ''; // "x-date-active";
19277             setCellClass(this, cells[i]);
19278         }
19279         var extraDays = 0;
19280         
19281         for(; i < 42; i++) {
19282             textEls[i].innerHTML = (++extraDays);
19283             d.setDate(d.getDate()+1);
19284             
19285             cells[i].className = "fc-future fc-other-month";
19286             setCellClass(this, cells[i]);
19287         }
19288         
19289         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19290         
19291         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19292         
19293         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19294         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19295         
19296         if(totalRows != 6){
19297             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19298             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19299         }
19300         
19301         this.fireEvent('monthchange', this, date);
19302         
19303         
19304         /*
19305         if(!this.internalRender){
19306             var main = this.el.dom.firstChild;
19307             var w = main.offsetWidth;
19308             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19309             Roo.fly(main).setWidth(w);
19310             this.internalRender = true;
19311             // opera does not respect the auto grow header center column
19312             // then, after it gets a width opera refuses to recalculate
19313             // without a second pass
19314             if(Roo.isOpera && !this.secondPass){
19315                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19316                 this.secondPass = true;
19317                 this.update.defer(10, this, [date]);
19318             }
19319         }
19320         */
19321         
19322     },
19323     
19324     findCell : function(dt) {
19325         dt = dt.clearTime().getTime();
19326         var ret = false;
19327         this.cells.each(function(c){
19328             //Roo.log("check " +c.dateValue + '?=' + dt);
19329             if(c.dateValue == dt){
19330                 ret = c;
19331                 return false;
19332             }
19333             return true;
19334         });
19335         
19336         return ret;
19337     },
19338     
19339     findCells : function(ev) {
19340         var s = ev.start.clone().clearTime().getTime();
19341        // Roo.log(s);
19342         var e= ev.end.clone().clearTime().getTime();
19343        // Roo.log(e);
19344         var ret = [];
19345         this.cells.each(function(c){
19346              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19347             
19348             if(c.dateValue > e){
19349                 return ;
19350             }
19351             if(c.dateValue < s){
19352                 return ;
19353             }
19354             ret.push(c);
19355         });
19356         
19357         return ret;    
19358     },
19359     
19360 //    findBestRow: function(cells)
19361 //    {
19362 //        var ret = 0;
19363 //        
19364 //        for (var i =0 ; i < cells.length;i++) {
19365 //            ret  = Math.max(cells[i].rows || 0,ret);
19366 //        }
19367 //        return ret;
19368 //        
19369 //    },
19370     
19371     
19372     addItem : function(ev)
19373     {
19374         // look for vertical location slot in
19375         var cells = this.findCells(ev);
19376         
19377 //        ev.row = this.findBestRow(cells);
19378         
19379         // work out the location.
19380         
19381         var crow = false;
19382         var rows = [];
19383         for(var i =0; i < cells.length; i++) {
19384             
19385             cells[i].row = cells[0].row;
19386             
19387             if(i == 0){
19388                 cells[i].row = cells[i].row + 1;
19389             }
19390             
19391             if (!crow) {
19392                 crow = {
19393                     start : cells[i],
19394                     end :  cells[i]
19395                 };
19396                 continue;
19397             }
19398             if (crow.start.getY() == cells[i].getY()) {
19399                 // on same row.
19400                 crow.end = cells[i];
19401                 continue;
19402             }
19403             // different row.
19404             rows.push(crow);
19405             crow = {
19406                 start: cells[i],
19407                 end : cells[i]
19408             };
19409             
19410         }
19411         
19412         rows.push(crow);
19413         ev.els = [];
19414         ev.rows = rows;
19415         ev.cells = cells;
19416         
19417         cells[0].events.push(ev);
19418         
19419         this.calevents.push(ev);
19420     },
19421     
19422     clearEvents: function() {
19423         
19424         if(!this.calevents){
19425             return;
19426         }
19427         
19428         Roo.each(this.cells.elements, function(c){
19429             c.row = 0;
19430             c.events = [];
19431             c.more = [];
19432         });
19433         
19434         Roo.each(this.calevents, function(e) {
19435             Roo.each(e.els, function(el) {
19436                 el.un('mouseenter' ,this.onEventEnter, this);
19437                 el.un('mouseleave' ,this.onEventLeave, this);
19438                 el.remove();
19439             },this);
19440         },this);
19441         
19442         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19443             e.remove();
19444         });
19445         
19446     },
19447     
19448     renderEvents: function()
19449     {   
19450         var _this = this;
19451         
19452         this.cells.each(function(c) {
19453             
19454             if(c.row < 5){
19455                 return;
19456             }
19457             
19458             var ev = c.events;
19459             
19460             var r = 4;
19461             if(c.row != c.events.length){
19462                 r = 4 - (4 - (c.row - c.events.length));
19463             }
19464             
19465             c.events = ev.slice(0, r);
19466             c.more = ev.slice(r);
19467             
19468             if(c.more.length && c.more.length == 1){
19469                 c.events.push(c.more.pop());
19470             }
19471             
19472             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19473             
19474         });
19475             
19476         this.cells.each(function(c) {
19477             
19478             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19479             
19480             
19481             for (var e = 0; e < c.events.length; e++){
19482                 var ev = c.events[e];
19483                 var rows = ev.rows;
19484                 
19485                 for(var i = 0; i < rows.length; i++) {
19486                 
19487                     // how many rows should it span..
19488
19489                     var  cfg = {
19490                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19491                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19492
19493                         unselectable : "on",
19494                         cn : [
19495                             {
19496                                 cls: 'fc-event-inner',
19497                                 cn : [
19498     //                                {
19499     //                                  tag:'span',
19500     //                                  cls: 'fc-event-time',
19501     //                                  html : cells.length > 1 ? '' : ev.time
19502     //                                },
19503                                     {
19504                                       tag:'span',
19505                                       cls: 'fc-event-title',
19506                                       html : String.format('{0}', ev.title)
19507                                     }
19508
19509
19510                                 ]
19511                             },
19512                             {
19513                                 cls: 'ui-resizable-handle ui-resizable-e',
19514                                 html : '&nbsp;&nbsp;&nbsp'
19515                             }
19516
19517                         ]
19518                     };
19519
19520                     if (i == 0) {
19521                         cfg.cls += ' fc-event-start';
19522                     }
19523                     if ((i+1) == rows.length) {
19524                         cfg.cls += ' fc-event-end';
19525                     }
19526
19527                     var ctr = _this.el.select('.fc-event-container',true).first();
19528                     var cg = ctr.createChild(cfg);
19529
19530                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19531                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19532
19533                     var r = (c.more.length) ? 1 : 0;
19534                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19535                     cg.setWidth(ebox.right - sbox.x -2);
19536
19537                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19538                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19539                     cg.on('click', _this.onEventClick, _this, ev);
19540
19541                     ev.els.push(cg);
19542                     
19543                 }
19544                 
19545             }
19546             
19547             
19548             if(c.more.length){
19549                 var  cfg = {
19550                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19551                     style : 'position: absolute',
19552                     unselectable : "on",
19553                     cn : [
19554                         {
19555                             cls: 'fc-event-inner',
19556                             cn : [
19557                                 {
19558                                   tag:'span',
19559                                   cls: 'fc-event-title',
19560                                   html : 'More'
19561                                 }
19562
19563
19564                             ]
19565                         },
19566                         {
19567                             cls: 'ui-resizable-handle ui-resizable-e',
19568                             html : '&nbsp;&nbsp;&nbsp'
19569                         }
19570
19571                     ]
19572                 };
19573
19574                 var ctr = _this.el.select('.fc-event-container',true).first();
19575                 var cg = ctr.createChild(cfg);
19576
19577                 var sbox = c.select('.fc-day-content',true).first().getBox();
19578                 var ebox = c.select('.fc-day-content',true).first().getBox();
19579                 //Roo.log(cg);
19580                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19581                 cg.setWidth(ebox.right - sbox.x -2);
19582
19583                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19584                 
19585             }
19586             
19587         });
19588         
19589         
19590         
19591     },
19592     
19593     onEventEnter: function (e, el,event,d) {
19594         this.fireEvent('evententer', this, el, event);
19595     },
19596     
19597     onEventLeave: function (e, el,event,d) {
19598         this.fireEvent('eventleave', this, el, event);
19599     },
19600     
19601     onEventClick: function (e, el,event,d) {
19602         this.fireEvent('eventclick', this, el, event);
19603     },
19604     
19605     onMonthChange: function () {
19606         this.store.load();
19607     },
19608     
19609     onMoreEventClick: function(e, el, more)
19610     {
19611         var _this = this;
19612         
19613         this.calpopover.placement = 'right';
19614         this.calpopover.setTitle('More');
19615         
19616         this.calpopover.setContent('');
19617         
19618         var ctr = this.calpopover.el.select('.popover-content', true).first();
19619         
19620         Roo.each(more, function(m){
19621             var cfg = {
19622                 cls : 'fc-event-hori fc-event-draggable',
19623                 html : m.title
19624             };
19625             var cg = ctr.createChild(cfg);
19626             
19627             cg.on('click', _this.onEventClick, _this, m);
19628         });
19629         
19630         this.calpopover.show(el);
19631         
19632         
19633     },
19634     
19635     onLoad: function () 
19636     {   
19637         this.calevents = [];
19638         var cal = this;
19639         
19640         if(this.store.getCount() > 0){
19641             this.store.data.each(function(d){
19642                cal.addItem({
19643                     id : d.data.id,
19644                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19645                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19646                     time : d.data.start_time,
19647                     title : d.data.title,
19648                     description : d.data.description,
19649                     venue : d.data.venue
19650                 });
19651             });
19652         }
19653         
19654         this.renderEvents();
19655         
19656         if(this.calevents.length && this.loadMask){
19657             this.maskEl.hide();
19658         }
19659     },
19660     
19661     onBeforeLoad: function()
19662     {
19663         this.clearEvents();
19664         if(this.loadMask){
19665             this.maskEl.show();
19666         }
19667     }
19668 });
19669
19670  
19671  /*
19672  * - LGPL
19673  *
19674  * element
19675  * 
19676  */
19677
19678 /**
19679  * @class Roo.bootstrap.Popover
19680  * @extends Roo.bootstrap.Component
19681  * Bootstrap Popover class
19682  * @cfg {String} html contents of the popover   (or false to use children..)
19683  * @cfg {String} title of popover (or false to hide)
19684  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19685  * @cfg {String} trigger click || hover (or false to trigger manually)
19686  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19687  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19688  *      - if false and it has a 'parent' then it will be automatically added to that element
19689  *      - if string - Roo.get  will be called 
19690  * @cfg {Number} delay - delay before showing
19691  
19692  * @constructor
19693  * Create a new Popover
19694  * @param {Object} config The config object
19695  */
19696
19697 Roo.bootstrap.Popover = function(config){
19698     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19699     
19700     this.addEvents({
19701         // raw events
19702          /**
19703          * @event show
19704          * After the popover show
19705          * 
19706          * @param {Roo.bootstrap.Popover} this
19707          */
19708         "show" : true,
19709         /**
19710          * @event hide
19711          * After the popover hide
19712          * 
19713          * @param {Roo.bootstrap.Popover} this
19714          */
19715         "hide" : true
19716     });
19717 };
19718
19719 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19720     
19721     title: false,
19722     html: false,
19723     
19724     placement : 'right',
19725     trigger : 'hover', // hover
19726     modal : false,
19727     delay : 0,
19728     
19729     over: false,
19730     
19731     can_build_overlaid : false,
19732     
19733     maskEl : false, // the mask element
19734     headerEl : false,
19735     contentEl : false,
19736     alignEl : false, // when show is called with an element - this get's stored.
19737     
19738     getChildContainer : function()
19739     {
19740         return this.contentEl;
19741         
19742     },
19743     getPopoverHeader : function()
19744     {
19745         this.title = true; // flag not to hide it..
19746         this.headerEl.addClass('p-0');
19747         return this.headerEl
19748     },
19749     
19750     
19751     getAutoCreate : function(){
19752          
19753         var cfg = {
19754            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19755            style: 'display:block',
19756            cn : [
19757                 {
19758                     cls : 'arrow'
19759                 },
19760                 {
19761                     cls : 'popover-inner ',
19762                     cn : [
19763                         {
19764                             tag: 'h3',
19765                             cls: 'popover-title popover-header',
19766                             html : this.title === false ? '' : this.title
19767                         },
19768                         {
19769                             cls : 'popover-content popover-body '  + (this.cls || ''),
19770                             html : this.html || ''
19771                         }
19772                     ]
19773                     
19774                 }
19775            ]
19776         };
19777         
19778         return cfg;
19779     },
19780     /**
19781      * @param {string} the title
19782      */
19783     setTitle: function(str)
19784     {
19785         this.title = str;
19786         if (this.el) {
19787             this.headerEl.dom.innerHTML = str;
19788         }
19789         
19790     },
19791     /**
19792      * @param {string} the body content
19793      */
19794     setContent: function(str)
19795     {
19796         this.html = str;
19797         if (this.contentEl) {
19798             this.contentEl.dom.innerHTML = str;
19799         }
19800         
19801     },
19802     // as it get's added to the bottom of the page.
19803     onRender : function(ct, position)
19804     {
19805         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19806         
19807         
19808         
19809         if(!this.el){
19810             var cfg = Roo.apply({},  this.getAutoCreate());
19811             cfg.id = Roo.id();
19812             
19813             if (this.cls) {
19814                 cfg.cls += ' ' + this.cls;
19815             }
19816             if (this.style) {
19817                 cfg.style = this.style;
19818             }
19819             //Roo.log("adding to ");
19820             this.el = Roo.get(document.body).createChild(cfg, position);
19821 //            Roo.log(this.el);
19822         }
19823         
19824         this.contentEl = this.el.select('.popover-content',true).first();
19825         this.headerEl =  this.el.select('.popover-title',true).first();
19826         
19827         var nitems = [];
19828         if(typeof(this.items) != 'undefined'){
19829             var items = this.items;
19830             delete this.items;
19831
19832             for(var i =0;i < items.length;i++) {
19833                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19834             }
19835         }
19836
19837         this.items = nitems;
19838         
19839         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19840         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19841         
19842         
19843         
19844         this.initEvents();
19845     },
19846     
19847     resizeMask : function()
19848     {
19849         this.maskEl.setSize(
19850             Roo.lib.Dom.getViewWidth(true),
19851             Roo.lib.Dom.getViewHeight(true)
19852         );
19853     },
19854     
19855     initEvents : function()
19856     {
19857         
19858         if (!this.modal) { 
19859             Roo.bootstrap.Popover.register(this);
19860         }
19861          
19862         this.arrowEl = this.el.select('.arrow',true).first();
19863         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19864         this.el.enableDisplayMode('block');
19865         this.el.hide();
19866  
19867         
19868         if (this.over === false && !this.parent()) {
19869             return; 
19870         }
19871         if (this.triggers === false) {
19872             return;
19873         }
19874          
19875         // support parent
19876         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19877         var triggers = this.trigger ? this.trigger.split(' ') : [];
19878         Roo.each(triggers, function(trigger) {
19879         
19880             if (trigger == 'click') {
19881                 on_el.on('click', this.toggle, this);
19882             } else if (trigger != 'manual') {
19883                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19884                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19885       
19886                 on_el.on(eventIn  ,this.enter, this);
19887                 on_el.on(eventOut, this.leave, this);
19888             }
19889         }, this);
19890     },
19891     
19892     
19893     // private
19894     timeout : null,
19895     hoverState : null,
19896     
19897     toggle : function () {
19898         this.hoverState == 'in' ? this.leave() : this.enter();
19899     },
19900     
19901     enter : function () {
19902         
19903         clearTimeout(this.timeout);
19904     
19905         this.hoverState = 'in';
19906     
19907         if (!this.delay || !this.delay.show) {
19908             this.show();
19909             return;
19910         }
19911         var _t = this;
19912         this.timeout = setTimeout(function () {
19913             if (_t.hoverState == 'in') {
19914                 _t.show();
19915             }
19916         }, this.delay.show)
19917     },
19918     
19919     leave : function() {
19920         clearTimeout(this.timeout);
19921     
19922         this.hoverState = 'out';
19923     
19924         if (!this.delay || !this.delay.hide) {
19925             this.hide();
19926             return;
19927         }
19928         var _t = this;
19929         this.timeout = setTimeout(function () {
19930             if (_t.hoverState == 'out') {
19931                 _t.hide();
19932             }
19933         }, this.delay.hide)
19934     },
19935     /**
19936      * Show the popover
19937      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19938      * @param {string} (left|right|top|bottom) position
19939      */
19940     show : function (on_el, placement)
19941     {
19942         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19943         on_el = on_el || false; // default to false
19944          
19945         if (!on_el) {
19946             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19947                 on_el = this.parent().el;
19948             } else if (this.over) {
19949                 Roo.get(this.over);
19950             }
19951             
19952         }
19953         
19954         this.alignEl = Roo.get( on_el );
19955
19956         if (!this.el) {
19957             this.render(document.body);
19958         }
19959         
19960         
19961          
19962         
19963         if (this.title === false) {
19964             this.headerEl.hide();
19965         }
19966         
19967        
19968         this.el.show();
19969         this.el.dom.style.display = 'block';
19970          
19971  
19972         if (this.alignEl) {
19973             this.updatePosition(this.placement, true);
19974              
19975         } else {
19976             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19977             var es = this.el.getSize();
19978             var x = Roo.lib.Dom.getViewWidth()/2;
19979             var y = Roo.lib.Dom.getViewHeight()/2;
19980             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19981             
19982         }
19983
19984         
19985         //var arrow = this.el.select('.arrow',true).first();
19986         //arrow.set(align[2], 
19987         
19988         this.el.addClass('in');
19989         
19990          
19991         
19992         this.hoverState = 'in';
19993         
19994         if (this.modal) {
19995             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19996             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19997             this.maskEl.dom.style.display = 'block';
19998             this.maskEl.addClass('show');
19999         }
20000         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20001  
20002         this.fireEvent('show', this);
20003         
20004     },
20005     /**
20006      * fire this manually after loading a grid in the table for example
20007      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20008      * @param {Boolean} try and move it if we cant get right position.
20009      */
20010     updatePosition : function(placement, try_move)
20011     {
20012         // allow for calling with no parameters
20013         placement = placement   ? placement :  this.placement;
20014         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20015         
20016         this.el.removeClass([
20017             'fade','top','bottom', 'left', 'right','in',
20018             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20019         ]);
20020         this.el.addClass(placement + ' bs-popover-' + placement);
20021         
20022         if (!this.alignEl ) {
20023             return false;
20024         }
20025         
20026         switch (placement) {
20027             case 'right':
20028                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20029                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20030                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20031                     //normal display... or moved up/down.
20032                     this.el.setXY(offset);
20033                     var xy = this.alignEl.getAnchorXY('tr', false);
20034                     xy[0]+=2;xy[1]+=5;
20035                     this.arrowEl.setXY(xy);
20036                     return true;
20037                 }
20038                 // continue through...
20039                 return this.updatePosition('left', false);
20040                 
20041             
20042             case 'left':
20043                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20044                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20045                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20046                     //normal display... or moved up/down.
20047                     this.el.setXY(offset);
20048                     var xy = this.alignEl.getAnchorXY('tl', false);
20049                     xy[0]-=10;xy[1]+=5; // << fix me
20050                     this.arrowEl.setXY(xy);
20051                     return true;
20052                 }
20053                 // call self...
20054                 return this.updatePosition('right', false);
20055             
20056             case 'top':
20057                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20058                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20059                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20060                     //normal display... or moved up/down.
20061                     this.el.setXY(offset);
20062                     var xy = this.alignEl.getAnchorXY('t', false);
20063                     xy[1]-=10; // << fix me
20064                     this.arrowEl.setXY(xy);
20065                     return true;
20066                 }
20067                 // fall through
20068                return this.updatePosition('bottom', false);
20069             
20070             case 'bottom':
20071                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20072                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20073                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20074                     //normal display... or moved up/down.
20075                     this.el.setXY(offset);
20076                     var xy = this.alignEl.getAnchorXY('b', false);
20077                      xy[1]+=2; // << fix me
20078                     this.arrowEl.setXY(xy);
20079                     return true;
20080                 }
20081                 // fall through
20082                 return this.updatePosition('top', false);
20083                 
20084             
20085         }
20086         
20087         
20088         return false;
20089     },
20090     
20091     hide : function()
20092     {
20093         this.el.setXY([0,0]);
20094         this.el.removeClass('in');
20095         this.el.hide();
20096         this.hoverState = null;
20097         this.maskEl.hide(); // always..
20098         this.fireEvent('hide', this);
20099     }
20100     
20101 });
20102
20103
20104 Roo.apply(Roo.bootstrap.Popover, {
20105
20106     alignment : {
20107         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20108         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20109         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20110         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20111     },
20112     
20113     zIndex : 20001,
20114
20115     clickHander : false,
20116     
20117
20118     onMouseDown : function(e)
20119     {
20120         if (!e.getTarget(".roo-popover")) {
20121             this.hideAll();
20122         }
20123          
20124     },
20125     
20126     popups : [],
20127     
20128     register : function(popup)
20129     {
20130         if (!Roo.bootstrap.Popover.clickHandler) {
20131             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20132         }
20133         // hide other popups.
20134         this.hideAll();
20135         this.popups.push(popup);
20136     },
20137     hideAll : function()
20138     {
20139         this.popups.forEach(function(p) {
20140             p.hide();
20141         });
20142     }
20143
20144 });/*
20145  * - LGPL
20146  *
20147  * Card header - holder for the card header elements.
20148  * 
20149  */
20150
20151 /**
20152  * @class Roo.bootstrap.PopoverNav
20153  * @extends Roo.bootstrap.NavGroup
20154  * Bootstrap Popover header navigation class
20155  * @constructor
20156  * Create a new Popover Header Navigation 
20157  * @param {Object} config The config object
20158  */
20159
20160 Roo.bootstrap.PopoverNav = function(config){
20161     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20162 };
20163
20164 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20165     
20166     
20167     container_method : 'getPopoverHeader' 
20168     
20169      
20170     
20171     
20172    
20173 });
20174
20175  
20176
20177  /*
20178  * - LGPL
20179  *
20180  * Progress
20181  * 
20182  */
20183
20184 /**
20185  * @class Roo.bootstrap.Progress
20186  * @extends Roo.bootstrap.Component
20187  * Bootstrap Progress class
20188  * @cfg {Boolean} striped striped of the progress bar
20189  * @cfg {Boolean} active animated of the progress bar
20190  * 
20191  * 
20192  * @constructor
20193  * Create a new Progress
20194  * @param {Object} config The config object
20195  */
20196
20197 Roo.bootstrap.Progress = function(config){
20198     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20199 };
20200
20201 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20202     
20203     striped : false,
20204     active: false,
20205     
20206     getAutoCreate : function(){
20207         var cfg = {
20208             tag: 'div',
20209             cls: 'progress'
20210         };
20211         
20212         
20213         if(this.striped){
20214             cfg.cls += ' progress-striped';
20215         }
20216       
20217         if(this.active){
20218             cfg.cls += ' active';
20219         }
20220         
20221         
20222         return cfg;
20223     }
20224    
20225 });
20226
20227  
20228
20229  /*
20230  * - LGPL
20231  *
20232  * ProgressBar
20233  * 
20234  */
20235
20236 /**
20237  * @class Roo.bootstrap.ProgressBar
20238  * @extends Roo.bootstrap.Component
20239  * Bootstrap ProgressBar class
20240  * @cfg {Number} aria_valuenow aria-value now
20241  * @cfg {Number} aria_valuemin aria-value min
20242  * @cfg {Number} aria_valuemax aria-value max
20243  * @cfg {String} label label for the progress bar
20244  * @cfg {String} panel (success | info | warning | danger )
20245  * @cfg {String} role role of the progress bar
20246  * @cfg {String} sr_only text
20247  * 
20248  * 
20249  * @constructor
20250  * Create a new ProgressBar
20251  * @param {Object} config The config object
20252  */
20253
20254 Roo.bootstrap.ProgressBar = function(config){
20255     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20256 };
20257
20258 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20259     
20260     aria_valuenow : 0,
20261     aria_valuemin : 0,
20262     aria_valuemax : 100,
20263     label : false,
20264     panel : false,
20265     role : false,
20266     sr_only: false,
20267     
20268     getAutoCreate : function()
20269     {
20270         
20271         var cfg = {
20272             tag: 'div',
20273             cls: 'progress-bar',
20274             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20275         };
20276         
20277         if(this.sr_only){
20278             cfg.cn = {
20279                 tag: 'span',
20280                 cls: 'sr-only',
20281                 html: this.sr_only
20282             }
20283         }
20284         
20285         if(this.role){
20286             cfg.role = this.role;
20287         }
20288         
20289         if(this.aria_valuenow){
20290             cfg['aria-valuenow'] = this.aria_valuenow;
20291         }
20292         
20293         if(this.aria_valuemin){
20294             cfg['aria-valuemin'] = this.aria_valuemin;
20295         }
20296         
20297         if(this.aria_valuemax){
20298             cfg['aria-valuemax'] = this.aria_valuemax;
20299         }
20300         
20301         if(this.label && !this.sr_only){
20302             cfg.html = this.label;
20303         }
20304         
20305         if(this.panel){
20306             cfg.cls += ' progress-bar-' + this.panel;
20307         }
20308         
20309         return cfg;
20310     },
20311     
20312     update : function(aria_valuenow)
20313     {
20314         this.aria_valuenow = aria_valuenow;
20315         
20316         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20317     }
20318    
20319 });
20320
20321  
20322
20323  /*
20324  * - LGPL
20325  *
20326  * column
20327  * 
20328  */
20329
20330 /**
20331  * @class Roo.bootstrap.TabGroup
20332  * @extends Roo.bootstrap.Column
20333  * Bootstrap Column class
20334  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20335  * @cfg {Boolean} carousel true to make the group behave like a carousel
20336  * @cfg {Boolean} bullets show bullets for the panels
20337  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20338  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20339  * @cfg {Boolean} showarrow (true|false) show arrow default true
20340  * 
20341  * @constructor
20342  * Create a new TabGroup
20343  * @param {Object} config The config object
20344  */
20345
20346 Roo.bootstrap.TabGroup = function(config){
20347     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20348     if (!this.navId) {
20349         this.navId = Roo.id();
20350     }
20351     this.tabs = [];
20352     Roo.bootstrap.TabGroup.register(this);
20353     
20354 };
20355
20356 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20357     
20358     carousel : false,
20359     transition : false,
20360     bullets : 0,
20361     timer : 0,
20362     autoslide : false,
20363     slideFn : false,
20364     slideOnTouch : false,
20365     showarrow : true,
20366     
20367     getAutoCreate : function()
20368     {
20369         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20370         
20371         cfg.cls += ' tab-content';
20372         
20373         if (this.carousel) {
20374             cfg.cls += ' carousel slide';
20375             
20376             cfg.cn = [{
20377                cls : 'carousel-inner',
20378                cn : []
20379             }];
20380         
20381             if(this.bullets  && !Roo.isTouch){
20382                 
20383                 var bullets = {
20384                     cls : 'carousel-bullets',
20385                     cn : []
20386                 };
20387                
20388                 if(this.bullets_cls){
20389                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20390                 }
20391                 
20392                 bullets.cn.push({
20393                     cls : 'clear'
20394                 });
20395                 
20396                 cfg.cn[0].cn.push(bullets);
20397             }
20398             
20399             if(this.showarrow){
20400                 cfg.cn[0].cn.push({
20401                     tag : 'div',
20402                     class : 'carousel-arrow',
20403                     cn : [
20404                         {
20405                             tag : 'div',
20406                             class : 'carousel-prev',
20407                             cn : [
20408                                 {
20409                                     tag : 'i',
20410                                     class : 'fa fa-chevron-left'
20411                                 }
20412                             ]
20413                         },
20414                         {
20415                             tag : 'div',
20416                             class : 'carousel-next',
20417                             cn : [
20418                                 {
20419                                     tag : 'i',
20420                                     class : 'fa fa-chevron-right'
20421                                 }
20422                             ]
20423                         }
20424                     ]
20425                 });
20426             }
20427             
20428         }
20429         
20430         return cfg;
20431     },
20432     
20433     initEvents:  function()
20434     {
20435 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20436 //            this.el.on("touchstart", this.onTouchStart, this);
20437 //        }
20438         
20439         if(this.autoslide){
20440             var _this = this;
20441             
20442             this.slideFn = window.setInterval(function() {
20443                 _this.showPanelNext();
20444             }, this.timer);
20445         }
20446         
20447         if(this.showarrow){
20448             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20449             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20450         }
20451         
20452         
20453     },
20454     
20455 //    onTouchStart : function(e, el, o)
20456 //    {
20457 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20458 //            return;
20459 //        }
20460 //        
20461 //        this.showPanelNext();
20462 //    },
20463     
20464     
20465     getChildContainer : function()
20466     {
20467         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20468     },
20469     
20470     /**
20471     * register a Navigation item
20472     * @param {Roo.bootstrap.NavItem} the navitem to add
20473     */
20474     register : function(item)
20475     {
20476         this.tabs.push( item);
20477         item.navId = this.navId; // not really needed..
20478         this.addBullet();
20479     
20480     },
20481     
20482     getActivePanel : function()
20483     {
20484         var r = false;
20485         Roo.each(this.tabs, function(t) {
20486             if (t.active) {
20487                 r = t;
20488                 return false;
20489             }
20490             return null;
20491         });
20492         return r;
20493         
20494     },
20495     getPanelByName : function(n)
20496     {
20497         var r = false;
20498         Roo.each(this.tabs, function(t) {
20499             if (t.tabId == n) {
20500                 r = t;
20501                 return false;
20502             }
20503             return null;
20504         });
20505         return r;
20506     },
20507     indexOfPanel : function(p)
20508     {
20509         var r = false;
20510         Roo.each(this.tabs, function(t,i) {
20511             if (t.tabId == p.tabId) {
20512                 r = i;
20513                 return false;
20514             }
20515             return null;
20516         });
20517         return r;
20518     },
20519     /**
20520      * show a specific panel
20521      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20522      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20523      */
20524     showPanel : function (pan)
20525     {
20526         if(this.transition || typeof(pan) == 'undefined'){
20527             Roo.log("waiting for the transitionend");
20528             return false;
20529         }
20530         
20531         if (typeof(pan) == 'number') {
20532             pan = this.tabs[pan];
20533         }
20534         
20535         if (typeof(pan) == 'string') {
20536             pan = this.getPanelByName(pan);
20537         }
20538         
20539         var cur = this.getActivePanel();
20540         
20541         if(!pan || !cur){
20542             Roo.log('pan or acitve pan is undefined');
20543             return false;
20544         }
20545         
20546         if (pan.tabId == this.getActivePanel().tabId) {
20547             return true;
20548         }
20549         
20550         if (false === cur.fireEvent('beforedeactivate')) {
20551             return false;
20552         }
20553         
20554         if(this.bullets > 0 && !Roo.isTouch){
20555             this.setActiveBullet(this.indexOfPanel(pan));
20556         }
20557         
20558         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20559             
20560             //class="carousel-item carousel-item-next carousel-item-left"
20561             
20562             this.transition = true;
20563             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20564             var lr = dir == 'next' ? 'left' : 'right';
20565             pan.el.addClass(dir); // or prev
20566             pan.el.addClass('carousel-item-' + dir); // or prev
20567             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20568             cur.el.addClass(lr); // or right
20569             pan.el.addClass(lr);
20570             cur.el.addClass('carousel-item-' +lr); // or right
20571             pan.el.addClass('carousel-item-' +lr);
20572             
20573             
20574             var _this = this;
20575             cur.el.on('transitionend', function() {
20576                 Roo.log("trans end?");
20577                 
20578                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20579                 pan.setActive(true);
20580                 
20581                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20582                 cur.setActive(false);
20583                 
20584                 _this.transition = false;
20585                 
20586             }, this, { single:  true } );
20587             
20588             return true;
20589         }
20590         
20591         cur.setActive(false);
20592         pan.setActive(true);
20593         
20594         return true;
20595         
20596     },
20597     showPanelNext : function()
20598     {
20599         var i = this.indexOfPanel(this.getActivePanel());
20600         
20601         if (i >= this.tabs.length - 1 && !this.autoslide) {
20602             return;
20603         }
20604         
20605         if (i >= this.tabs.length - 1 && this.autoslide) {
20606             i = -1;
20607         }
20608         
20609         this.showPanel(this.tabs[i+1]);
20610     },
20611     
20612     showPanelPrev : function()
20613     {
20614         var i = this.indexOfPanel(this.getActivePanel());
20615         
20616         if (i  < 1 && !this.autoslide) {
20617             return;
20618         }
20619         
20620         if (i < 1 && this.autoslide) {
20621             i = this.tabs.length;
20622         }
20623         
20624         this.showPanel(this.tabs[i-1]);
20625     },
20626     
20627     
20628     addBullet: function()
20629     {
20630         if(!this.bullets || Roo.isTouch){
20631             return;
20632         }
20633         var ctr = this.el.select('.carousel-bullets',true).first();
20634         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20635         var bullet = ctr.createChild({
20636             cls : 'bullet bullet-' + i
20637         },ctr.dom.lastChild);
20638         
20639         
20640         var _this = this;
20641         
20642         bullet.on('click', (function(e, el, o, ii, t){
20643
20644             e.preventDefault();
20645
20646             this.showPanel(ii);
20647
20648             if(this.autoslide && this.slideFn){
20649                 clearInterval(this.slideFn);
20650                 this.slideFn = window.setInterval(function() {
20651                     _this.showPanelNext();
20652                 }, this.timer);
20653             }
20654
20655         }).createDelegate(this, [i, bullet], true));
20656                 
20657         
20658     },
20659      
20660     setActiveBullet : function(i)
20661     {
20662         if(Roo.isTouch){
20663             return;
20664         }
20665         
20666         Roo.each(this.el.select('.bullet', true).elements, function(el){
20667             el.removeClass('selected');
20668         });
20669
20670         var bullet = this.el.select('.bullet-' + i, true).first();
20671         
20672         if(!bullet){
20673             return;
20674         }
20675         
20676         bullet.addClass('selected');
20677     }
20678     
20679     
20680   
20681 });
20682
20683  
20684
20685  
20686  
20687 Roo.apply(Roo.bootstrap.TabGroup, {
20688     
20689     groups: {},
20690      /**
20691     * register a Navigation Group
20692     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20693     */
20694     register : function(navgrp)
20695     {
20696         this.groups[navgrp.navId] = navgrp;
20697         
20698     },
20699     /**
20700     * fetch a Navigation Group based on the navigation ID
20701     * if one does not exist , it will get created.
20702     * @param {string} the navgroup to add
20703     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20704     */
20705     get: function(navId) {
20706         if (typeof(this.groups[navId]) == 'undefined') {
20707             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20708         }
20709         return this.groups[navId] ;
20710     }
20711     
20712     
20713     
20714 });
20715
20716  /*
20717  * - LGPL
20718  *
20719  * TabPanel
20720  * 
20721  */
20722
20723 /**
20724  * @class Roo.bootstrap.TabPanel
20725  * @extends Roo.bootstrap.Component
20726  * Bootstrap TabPanel class
20727  * @cfg {Boolean} active panel active
20728  * @cfg {String} html panel content
20729  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20730  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20731  * @cfg {String} href click to link..
20732  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20733  * 
20734  * 
20735  * @constructor
20736  * Create a new TabPanel
20737  * @param {Object} config The config object
20738  */
20739
20740 Roo.bootstrap.TabPanel = function(config){
20741     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20742     this.addEvents({
20743         /**
20744              * @event changed
20745              * Fires when the active status changes
20746              * @param {Roo.bootstrap.TabPanel} this
20747              * @param {Boolean} state the new state
20748             
20749          */
20750         'changed': true,
20751         /**
20752              * @event beforedeactivate
20753              * Fires before a tab is de-activated - can be used to do validation on a form.
20754              * @param {Roo.bootstrap.TabPanel} this
20755              * @return {Boolean} false if there is an error
20756             
20757          */
20758         'beforedeactivate': true
20759      });
20760     
20761     this.tabId = this.tabId || Roo.id();
20762   
20763 };
20764
20765 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20766     
20767     active: false,
20768     html: false,
20769     tabId: false,
20770     navId : false,
20771     href : '',
20772     touchSlide : false,
20773     getAutoCreate : function(){
20774         
20775         
20776         var cfg = {
20777             tag: 'div',
20778             // item is needed for carousel - not sure if it has any effect otherwise
20779             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20780             html: this.html || ''
20781         };
20782         
20783         if(this.active){
20784             cfg.cls += ' active';
20785         }
20786         
20787         if(this.tabId){
20788             cfg.tabId = this.tabId;
20789         }
20790         
20791         
20792         
20793         return cfg;
20794     },
20795     
20796     initEvents:  function()
20797     {
20798         var p = this.parent();
20799         
20800         this.navId = this.navId || p.navId;
20801         
20802         if (typeof(this.navId) != 'undefined') {
20803             // not really needed.. but just in case.. parent should be a NavGroup.
20804             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20805             
20806             tg.register(this);
20807             
20808             var i = tg.tabs.length - 1;
20809             
20810             if(this.active && tg.bullets > 0 && i < tg.bullets){
20811                 tg.setActiveBullet(i);
20812             }
20813         }
20814         
20815         this.el.on('click', this.onClick, this);
20816         
20817         if(Roo.isTouch && this.touchSlide){
20818             this.el.on("touchstart", this.onTouchStart, this);
20819             this.el.on("touchmove", this.onTouchMove, this);
20820             this.el.on("touchend", this.onTouchEnd, this);
20821         }
20822         
20823     },
20824     
20825     onRender : function(ct, position)
20826     {
20827         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20828     },
20829     
20830     setActive : function(state)
20831     {
20832         Roo.log("panel - set active " + this.tabId + "=" + state);
20833         
20834         this.active = state;
20835         if (!state) {
20836             this.el.removeClass('active');
20837             
20838         } else  if (!this.el.hasClass('active')) {
20839             this.el.addClass('active');
20840         }
20841         
20842         this.fireEvent('changed', this, state);
20843     },
20844     
20845     onClick : function(e)
20846     {
20847         e.preventDefault();
20848         
20849         if(!this.href.length){
20850             return;
20851         }
20852         
20853         window.location.href = this.href;
20854     },
20855     
20856     startX : 0,
20857     startY : 0,
20858     endX : 0,
20859     endY : 0,
20860     swiping : false,
20861     
20862     onTouchStart : function(e)
20863     {
20864         this.swiping = false;
20865         
20866         this.startX = e.browserEvent.touches[0].clientX;
20867         this.startY = e.browserEvent.touches[0].clientY;
20868     },
20869     
20870     onTouchMove : function(e)
20871     {
20872         this.swiping = true;
20873         
20874         this.endX = e.browserEvent.touches[0].clientX;
20875         this.endY = e.browserEvent.touches[0].clientY;
20876     },
20877     
20878     onTouchEnd : function(e)
20879     {
20880         if(!this.swiping){
20881             this.onClick(e);
20882             return;
20883         }
20884         
20885         var tabGroup = this.parent();
20886         
20887         if(this.endX > this.startX){ // swiping right
20888             tabGroup.showPanelPrev();
20889             return;
20890         }
20891         
20892         if(this.startX > this.endX){ // swiping left
20893             tabGroup.showPanelNext();
20894             return;
20895         }
20896     }
20897     
20898     
20899 });
20900  
20901
20902  
20903
20904  /*
20905  * - LGPL
20906  *
20907  * DateField
20908  * 
20909  */
20910
20911 /**
20912  * @class Roo.bootstrap.DateField
20913  * @extends Roo.bootstrap.Input
20914  * Bootstrap DateField class
20915  * @cfg {Number} weekStart default 0
20916  * @cfg {String} viewMode default empty, (months|years)
20917  * @cfg {String} minViewMode default empty, (months|years)
20918  * @cfg {Number} startDate default -Infinity
20919  * @cfg {Number} endDate default Infinity
20920  * @cfg {Boolean} todayHighlight default false
20921  * @cfg {Boolean} todayBtn default false
20922  * @cfg {Boolean} calendarWeeks default false
20923  * @cfg {Object} daysOfWeekDisabled default empty
20924  * @cfg {Boolean} singleMode default false (true | false)
20925  * 
20926  * @cfg {Boolean} keyboardNavigation default true
20927  * @cfg {String} language default en
20928  * 
20929  * @constructor
20930  * Create a new DateField
20931  * @param {Object} config The config object
20932  */
20933
20934 Roo.bootstrap.DateField = function(config){
20935     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20936      this.addEvents({
20937             /**
20938              * @event show
20939              * Fires when this field show.
20940              * @param {Roo.bootstrap.DateField} this
20941              * @param {Mixed} date The date value
20942              */
20943             show : true,
20944             /**
20945              * @event show
20946              * Fires when this field hide.
20947              * @param {Roo.bootstrap.DateField} this
20948              * @param {Mixed} date The date value
20949              */
20950             hide : true,
20951             /**
20952              * @event select
20953              * Fires when select a date.
20954              * @param {Roo.bootstrap.DateField} this
20955              * @param {Mixed} date The date value
20956              */
20957             select : true,
20958             /**
20959              * @event beforeselect
20960              * Fires when before select a date.
20961              * @param {Roo.bootstrap.DateField} this
20962              * @param {Mixed} date The date value
20963              */
20964             beforeselect : true
20965         });
20966 };
20967
20968 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20969     
20970     /**
20971      * @cfg {String} format
20972      * The default date format string which can be overriden for localization support.  The format must be
20973      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20974      */
20975     format : "m/d/y",
20976     /**
20977      * @cfg {String} altFormats
20978      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20979      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20980      */
20981     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20982     
20983     weekStart : 0,
20984     
20985     viewMode : '',
20986     
20987     minViewMode : '',
20988     
20989     todayHighlight : false,
20990     
20991     todayBtn: false,
20992     
20993     language: 'en',
20994     
20995     keyboardNavigation: true,
20996     
20997     calendarWeeks: false,
20998     
20999     startDate: -Infinity,
21000     
21001     endDate: Infinity,
21002     
21003     daysOfWeekDisabled: [],
21004     
21005     _events: [],
21006     
21007     singleMode : false,
21008     
21009     UTCDate: function()
21010     {
21011         return new Date(Date.UTC.apply(Date, arguments));
21012     },
21013     
21014     UTCToday: function()
21015     {
21016         var today = new Date();
21017         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21018     },
21019     
21020     getDate: function() {
21021             var d = this.getUTCDate();
21022             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21023     },
21024     
21025     getUTCDate: function() {
21026             return this.date;
21027     },
21028     
21029     setDate: function(d) {
21030             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21031     },
21032     
21033     setUTCDate: function(d) {
21034             this.date = d;
21035             this.setValue(this.formatDate(this.date));
21036     },
21037         
21038     onRender: function(ct, position)
21039     {
21040         
21041         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21042         
21043         this.language = this.language || 'en';
21044         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21045         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21046         
21047         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21048         this.format = this.format || 'm/d/y';
21049         this.isInline = false;
21050         this.isInput = true;
21051         this.component = this.el.select('.add-on', true).first() || false;
21052         this.component = (this.component && this.component.length === 0) ? false : this.component;
21053         this.hasInput = this.component && this.inputEl().length;
21054         
21055         if (typeof(this.minViewMode === 'string')) {
21056             switch (this.minViewMode) {
21057                 case 'months':
21058                     this.minViewMode = 1;
21059                     break;
21060                 case 'years':
21061                     this.minViewMode = 2;
21062                     break;
21063                 default:
21064                     this.minViewMode = 0;
21065                     break;
21066             }
21067         }
21068         
21069         if (typeof(this.viewMode === 'string')) {
21070             switch (this.viewMode) {
21071                 case 'months':
21072                     this.viewMode = 1;
21073                     break;
21074                 case 'years':
21075                     this.viewMode = 2;
21076                     break;
21077                 default:
21078                     this.viewMode = 0;
21079                     break;
21080             }
21081         }
21082                 
21083         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21084         
21085 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21086         
21087         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21088         
21089         this.picker().on('mousedown', this.onMousedown, this);
21090         this.picker().on('click', this.onClick, this);
21091         
21092         this.picker().addClass('datepicker-dropdown');
21093         
21094         this.startViewMode = this.viewMode;
21095         
21096         if(this.singleMode){
21097             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21098                 v.setVisibilityMode(Roo.Element.DISPLAY);
21099                 v.hide();
21100             });
21101             
21102             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21103                 v.setStyle('width', '189px');
21104             });
21105         }
21106         
21107         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21108             if(!this.calendarWeeks){
21109                 v.remove();
21110                 return;
21111             }
21112             
21113             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21114             v.attr('colspan', function(i, val){
21115                 return parseInt(val) + 1;
21116             });
21117         });
21118                         
21119         
21120         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21121         
21122         this.setStartDate(this.startDate);
21123         this.setEndDate(this.endDate);
21124         
21125         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21126         
21127         this.fillDow();
21128         this.fillMonths();
21129         this.update();
21130         this.showMode();
21131         
21132         if(this.isInline) {
21133             this.showPopup();
21134         }
21135     },
21136     
21137     picker : function()
21138     {
21139         return this.pickerEl;
21140 //        return this.el.select('.datepicker', true).first();
21141     },
21142     
21143     fillDow: function()
21144     {
21145         var dowCnt = this.weekStart;
21146         
21147         var dow = {
21148             tag: 'tr',
21149             cn: [
21150                 
21151             ]
21152         };
21153         
21154         if(this.calendarWeeks){
21155             dow.cn.push({
21156                 tag: 'th',
21157                 cls: 'cw',
21158                 html: '&nbsp;'
21159             })
21160         }
21161         
21162         while (dowCnt < this.weekStart + 7) {
21163             dow.cn.push({
21164                 tag: 'th',
21165                 cls: 'dow',
21166                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21167             });
21168         }
21169         
21170         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21171     },
21172     
21173     fillMonths: function()
21174     {    
21175         var i = 0;
21176         var months = this.picker().select('>.datepicker-months td', true).first();
21177         
21178         months.dom.innerHTML = '';
21179         
21180         while (i < 12) {
21181             var month = {
21182                 tag: 'span',
21183                 cls: 'month',
21184                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21185             };
21186             
21187             months.createChild(month);
21188         }
21189         
21190     },
21191     
21192     update: function()
21193     {
21194         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;
21195         
21196         if (this.date < this.startDate) {
21197             this.viewDate = new Date(this.startDate);
21198         } else if (this.date > this.endDate) {
21199             this.viewDate = new Date(this.endDate);
21200         } else {
21201             this.viewDate = new Date(this.date);
21202         }
21203         
21204         this.fill();
21205     },
21206     
21207     fill: function() 
21208     {
21209         var d = new Date(this.viewDate),
21210                 year = d.getUTCFullYear(),
21211                 month = d.getUTCMonth(),
21212                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21213                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21214                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21215                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21216                 currentDate = this.date && this.date.valueOf(),
21217                 today = this.UTCToday();
21218         
21219         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21220         
21221 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21222         
21223 //        this.picker.select('>tfoot th.today').
21224 //                                              .text(dates[this.language].today)
21225 //                                              .toggle(this.todayBtn !== false);
21226     
21227         this.updateNavArrows();
21228         this.fillMonths();
21229                                                 
21230         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21231         
21232         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21233          
21234         prevMonth.setUTCDate(day);
21235         
21236         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21237         
21238         var nextMonth = new Date(prevMonth);
21239         
21240         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21241         
21242         nextMonth = nextMonth.valueOf();
21243         
21244         var fillMonths = false;
21245         
21246         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21247         
21248         while(prevMonth.valueOf() <= nextMonth) {
21249             var clsName = '';
21250             
21251             if (prevMonth.getUTCDay() === this.weekStart) {
21252                 if(fillMonths){
21253                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21254                 }
21255                     
21256                 fillMonths = {
21257                     tag: 'tr',
21258                     cn: []
21259                 };
21260                 
21261                 if(this.calendarWeeks){
21262                     // ISO 8601: First week contains first thursday.
21263                     // ISO also states week starts on Monday, but we can be more abstract here.
21264                     var
21265                     // Start of current week: based on weekstart/current date
21266                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21267                     // Thursday of this week
21268                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21269                     // First Thursday of year, year from thursday
21270                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21271                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21272                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21273                     
21274                     fillMonths.cn.push({
21275                         tag: 'td',
21276                         cls: 'cw',
21277                         html: calWeek
21278                     });
21279                 }
21280             }
21281             
21282             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21283                 clsName += ' old';
21284             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21285                 clsName += ' new';
21286             }
21287             if (this.todayHighlight &&
21288                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21289                 prevMonth.getUTCMonth() == today.getMonth() &&
21290                 prevMonth.getUTCDate() == today.getDate()) {
21291                 clsName += ' today';
21292             }
21293             
21294             if (currentDate && prevMonth.valueOf() === currentDate) {
21295                 clsName += ' active';
21296             }
21297             
21298             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21299                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21300                     clsName += ' disabled';
21301             }
21302             
21303             fillMonths.cn.push({
21304                 tag: 'td',
21305                 cls: 'day ' + clsName,
21306                 html: prevMonth.getDate()
21307             });
21308             
21309             prevMonth.setDate(prevMonth.getDate()+1);
21310         }
21311           
21312         var currentYear = this.date && this.date.getUTCFullYear();
21313         var currentMonth = this.date && this.date.getUTCMonth();
21314         
21315         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21316         
21317         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21318             v.removeClass('active');
21319             
21320             if(currentYear === year && k === currentMonth){
21321                 v.addClass('active');
21322             }
21323             
21324             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21325                 v.addClass('disabled');
21326             }
21327             
21328         });
21329         
21330         
21331         year = parseInt(year/10, 10) * 10;
21332         
21333         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21334         
21335         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21336         
21337         year -= 1;
21338         for (var i = -1; i < 11; i++) {
21339             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21340                 tag: 'span',
21341                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21342                 html: year
21343             });
21344             
21345             year += 1;
21346         }
21347     },
21348     
21349     showMode: function(dir) 
21350     {
21351         if (dir) {
21352             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21353         }
21354         
21355         Roo.each(this.picker().select('>div',true).elements, function(v){
21356             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21357             v.hide();
21358         });
21359         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21360     },
21361     
21362     place: function()
21363     {
21364         if(this.isInline) {
21365             return;
21366         }
21367         
21368         this.picker().removeClass(['bottom', 'top']);
21369         
21370         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21371             /*
21372              * place to the top of element!
21373              *
21374              */
21375             
21376             this.picker().addClass('top');
21377             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21378             
21379             return;
21380         }
21381         
21382         this.picker().addClass('bottom');
21383         
21384         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21385     },
21386     
21387     parseDate : function(value)
21388     {
21389         if(!value || value instanceof Date){
21390             return value;
21391         }
21392         var v = Date.parseDate(value, this.format);
21393         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21394             v = Date.parseDate(value, 'Y-m-d');
21395         }
21396         if(!v && this.altFormats){
21397             if(!this.altFormatsArray){
21398                 this.altFormatsArray = this.altFormats.split("|");
21399             }
21400             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21401                 v = Date.parseDate(value, this.altFormatsArray[i]);
21402             }
21403         }
21404         return v;
21405     },
21406     
21407     formatDate : function(date, fmt)
21408     {   
21409         return (!date || !(date instanceof Date)) ?
21410         date : date.dateFormat(fmt || this.format);
21411     },
21412     
21413     onFocus : function()
21414     {
21415         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21416         this.showPopup();
21417     },
21418     
21419     onBlur : function()
21420     {
21421         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21422         
21423         var d = this.inputEl().getValue();
21424         
21425         this.setValue(d);
21426                 
21427         this.hidePopup();
21428     },
21429     
21430     showPopup : function()
21431     {
21432         this.picker().show();
21433         this.update();
21434         this.place();
21435         
21436         this.fireEvent('showpopup', this, this.date);
21437     },
21438     
21439     hidePopup : function()
21440     {
21441         if(this.isInline) {
21442             return;
21443         }
21444         this.picker().hide();
21445         this.viewMode = this.startViewMode;
21446         this.showMode();
21447         
21448         this.fireEvent('hidepopup', this, this.date);
21449         
21450     },
21451     
21452     onMousedown: function(e)
21453     {
21454         e.stopPropagation();
21455         e.preventDefault();
21456     },
21457     
21458     keyup: function(e)
21459     {
21460         Roo.bootstrap.DateField.superclass.keyup.call(this);
21461         this.update();
21462     },
21463
21464     setValue: function(v)
21465     {
21466         if(this.fireEvent('beforeselect', this, v) !== false){
21467             var d = new Date(this.parseDate(v) ).clearTime();
21468         
21469             if(isNaN(d.getTime())){
21470                 this.date = this.viewDate = '';
21471                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21472                 return;
21473             }
21474
21475             v = this.formatDate(d);
21476
21477             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21478
21479             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21480
21481             this.update();
21482
21483             this.fireEvent('select', this, this.date);
21484         }
21485     },
21486     
21487     getValue: function()
21488     {
21489         return this.formatDate(this.date);
21490     },
21491     
21492     fireKey: function(e)
21493     {
21494         if (!this.picker().isVisible()){
21495             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21496                 this.showPopup();
21497             }
21498             return;
21499         }
21500         
21501         var dateChanged = false,
21502         dir, day, month,
21503         newDate, newViewDate;
21504         
21505         switch(e.keyCode){
21506             case 27: // escape
21507                 this.hidePopup();
21508                 e.preventDefault();
21509                 break;
21510             case 37: // left
21511             case 39: // right
21512                 if (!this.keyboardNavigation) {
21513                     break;
21514                 }
21515                 dir = e.keyCode == 37 ? -1 : 1;
21516                 
21517                 if (e.ctrlKey){
21518                     newDate = this.moveYear(this.date, dir);
21519                     newViewDate = this.moveYear(this.viewDate, dir);
21520                 } else if (e.shiftKey){
21521                     newDate = this.moveMonth(this.date, dir);
21522                     newViewDate = this.moveMonth(this.viewDate, dir);
21523                 } else {
21524                     newDate = new Date(this.date);
21525                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21526                     newViewDate = new Date(this.viewDate);
21527                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21528                 }
21529                 if (this.dateWithinRange(newDate)){
21530                     this.date = newDate;
21531                     this.viewDate = newViewDate;
21532                     this.setValue(this.formatDate(this.date));
21533 //                    this.update();
21534                     e.preventDefault();
21535                     dateChanged = true;
21536                 }
21537                 break;
21538             case 38: // up
21539             case 40: // down
21540                 if (!this.keyboardNavigation) {
21541                     break;
21542                 }
21543                 dir = e.keyCode == 38 ? -1 : 1;
21544                 if (e.ctrlKey){
21545                     newDate = this.moveYear(this.date, dir);
21546                     newViewDate = this.moveYear(this.viewDate, dir);
21547                 } else if (e.shiftKey){
21548                     newDate = this.moveMonth(this.date, dir);
21549                     newViewDate = this.moveMonth(this.viewDate, dir);
21550                 } else {
21551                     newDate = new Date(this.date);
21552                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21553                     newViewDate = new Date(this.viewDate);
21554                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21555                 }
21556                 if (this.dateWithinRange(newDate)){
21557                     this.date = newDate;
21558                     this.viewDate = newViewDate;
21559                     this.setValue(this.formatDate(this.date));
21560 //                    this.update();
21561                     e.preventDefault();
21562                     dateChanged = true;
21563                 }
21564                 break;
21565             case 13: // enter
21566                 this.setValue(this.formatDate(this.date));
21567                 this.hidePopup();
21568                 e.preventDefault();
21569                 break;
21570             case 9: // tab
21571                 this.setValue(this.formatDate(this.date));
21572                 this.hidePopup();
21573                 break;
21574             case 16: // shift
21575             case 17: // ctrl
21576             case 18: // alt
21577                 break;
21578             default :
21579                 this.hidePopup();
21580                 
21581         }
21582     },
21583     
21584     
21585     onClick: function(e) 
21586     {
21587         e.stopPropagation();
21588         e.preventDefault();
21589         
21590         var target = e.getTarget();
21591         
21592         if(target.nodeName.toLowerCase() === 'i'){
21593             target = Roo.get(target).dom.parentNode;
21594         }
21595         
21596         var nodeName = target.nodeName;
21597         var className = target.className;
21598         var html = target.innerHTML;
21599         //Roo.log(nodeName);
21600         
21601         switch(nodeName.toLowerCase()) {
21602             case 'th':
21603                 switch(className) {
21604                     case 'switch':
21605                         this.showMode(1);
21606                         break;
21607                     case 'prev':
21608                     case 'next':
21609                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21610                         switch(this.viewMode){
21611                                 case 0:
21612                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21613                                         break;
21614                                 case 1:
21615                                 case 2:
21616                                         this.viewDate = this.moveYear(this.viewDate, dir);
21617                                         break;
21618                         }
21619                         this.fill();
21620                         break;
21621                     case 'today':
21622                         var date = new Date();
21623                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21624 //                        this.fill()
21625                         this.setValue(this.formatDate(this.date));
21626                         
21627                         this.hidePopup();
21628                         break;
21629                 }
21630                 break;
21631             case 'span':
21632                 if (className.indexOf('disabled') < 0) {
21633                     this.viewDate.setUTCDate(1);
21634                     if (className.indexOf('month') > -1) {
21635                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21636                     } else {
21637                         var year = parseInt(html, 10) || 0;
21638                         this.viewDate.setUTCFullYear(year);
21639                         
21640                     }
21641                     
21642                     if(this.singleMode){
21643                         this.setValue(this.formatDate(this.viewDate));
21644                         this.hidePopup();
21645                         return;
21646                     }
21647                     
21648                     this.showMode(-1);
21649                     this.fill();
21650                 }
21651                 break;
21652                 
21653             case 'td':
21654                 //Roo.log(className);
21655                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21656                     var day = parseInt(html, 10) || 1;
21657                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21658                         month = (this.viewDate || new Date()).getUTCMonth();
21659
21660                     if (className.indexOf('old') > -1) {
21661                         if(month === 0 ){
21662                             month = 11;
21663                             year -= 1;
21664                         }else{
21665                             month -= 1;
21666                         }
21667                     } else if (className.indexOf('new') > -1) {
21668                         if (month == 11) {
21669                             month = 0;
21670                             year += 1;
21671                         } else {
21672                             month += 1;
21673                         }
21674                     }
21675                     //Roo.log([year,month,day]);
21676                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21677                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21678 //                    this.fill();
21679                     //Roo.log(this.formatDate(this.date));
21680                     this.setValue(this.formatDate(this.date));
21681                     this.hidePopup();
21682                 }
21683                 break;
21684         }
21685     },
21686     
21687     setStartDate: function(startDate)
21688     {
21689         this.startDate = startDate || -Infinity;
21690         if (this.startDate !== -Infinity) {
21691             this.startDate = this.parseDate(this.startDate);
21692         }
21693         this.update();
21694         this.updateNavArrows();
21695     },
21696
21697     setEndDate: function(endDate)
21698     {
21699         this.endDate = endDate || Infinity;
21700         if (this.endDate !== Infinity) {
21701             this.endDate = this.parseDate(this.endDate);
21702         }
21703         this.update();
21704         this.updateNavArrows();
21705     },
21706     
21707     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21708     {
21709         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21710         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21711             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21712         }
21713         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21714             return parseInt(d, 10);
21715         });
21716         this.update();
21717         this.updateNavArrows();
21718     },
21719     
21720     updateNavArrows: function() 
21721     {
21722         if(this.singleMode){
21723             return;
21724         }
21725         
21726         var d = new Date(this.viewDate),
21727         year = d.getUTCFullYear(),
21728         month = d.getUTCMonth();
21729         
21730         Roo.each(this.picker().select('.prev', true).elements, function(v){
21731             v.show();
21732             switch (this.viewMode) {
21733                 case 0:
21734
21735                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21736                         v.hide();
21737                     }
21738                     break;
21739                 case 1:
21740                 case 2:
21741                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21742                         v.hide();
21743                     }
21744                     break;
21745             }
21746         });
21747         
21748         Roo.each(this.picker().select('.next', true).elements, function(v){
21749             v.show();
21750             switch (this.viewMode) {
21751                 case 0:
21752
21753                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21754                         v.hide();
21755                     }
21756                     break;
21757                 case 1:
21758                 case 2:
21759                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21760                         v.hide();
21761                     }
21762                     break;
21763             }
21764         })
21765     },
21766     
21767     moveMonth: function(date, dir)
21768     {
21769         if (!dir) {
21770             return date;
21771         }
21772         var new_date = new Date(date.valueOf()),
21773         day = new_date.getUTCDate(),
21774         month = new_date.getUTCMonth(),
21775         mag = Math.abs(dir),
21776         new_month, test;
21777         dir = dir > 0 ? 1 : -1;
21778         if (mag == 1){
21779             test = dir == -1
21780             // If going back one month, make sure month is not current month
21781             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21782             ? function(){
21783                 return new_date.getUTCMonth() == month;
21784             }
21785             // If going forward one month, make sure month is as expected
21786             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21787             : function(){
21788                 return new_date.getUTCMonth() != new_month;
21789             };
21790             new_month = month + dir;
21791             new_date.setUTCMonth(new_month);
21792             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21793             if (new_month < 0 || new_month > 11) {
21794                 new_month = (new_month + 12) % 12;
21795             }
21796         } else {
21797             // For magnitudes >1, move one month at a time...
21798             for (var i=0; i<mag; i++) {
21799                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21800                 new_date = this.moveMonth(new_date, dir);
21801             }
21802             // ...then reset the day, keeping it in the new month
21803             new_month = new_date.getUTCMonth();
21804             new_date.setUTCDate(day);
21805             test = function(){
21806                 return new_month != new_date.getUTCMonth();
21807             };
21808         }
21809         // Common date-resetting loop -- if date is beyond end of month, make it
21810         // end of month
21811         while (test()){
21812             new_date.setUTCDate(--day);
21813             new_date.setUTCMonth(new_month);
21814         }
21815         return new_date;
21816     },
21817
21818     moveYear: function(date, dir)
21819     {
21820         return this.moveMonth(date, dir*12);
21821     },
21822
21823     dateWithinRange: function(date)
21824     {
21825         return date >= this.startDate && date <= this.endDate;
21826     },
21827
21828     
21829     remove: function() 
21830     {
21831         this.picker().remove();
21832     },
21833     
21834     validateValue : function(value)
21835     {
21836         if(this.getVisibilityEl().hasClass('hidden')){
21837             return true;
21838         }
21839         
21840         if(value.length < 1)  {
21841             if(this.allowBlank){
21842                 return true;
21843             }
21844             return false;
21845         }
21846         
21847         if(value.length < this.minLength){
21848             return false;
21849         }
21850         if(value.length > this.maxLength){
21851             return false;
21852         }
21853         if(this.vtype){
21854             var vt = Roo.form.VTypes;
21855             if(!vt[this.vtype](value, this)){
21856                 return false;
21857             }
21858         }
21859         if(typeof this.validator == "function"){
21860             var msg = this.validator(value);
21861             if(msg !== true){
21862                 return false;
21863             }
21864         }
21865         
21866         if(this.regex && !this.regex.test(value)){
21867             return false;
21868         }
21869         
21870         if(typeof(this.parseDate(value)) == 'undefined'){
21871             return false;
21872         }
21873         
21874         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21875             return false;
21876         }      
21877         
21878         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21879             return false;
21880         } 
21881         
21882         
21883         return true;
21884     },
21885     
21886     reset : function()
21887     {
21888         this.date = this.viewDate = '';
21889         
21890         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21891     }
21892    
21893 });
21894
21895 Roo.apply(Roo.bootstrap.DateField,  {
21896     
21897     head : {
21898         tag: 'thead',
21899         cn: [
21900         {
21901             tag: 'tr',
21902             cn: [
21903             {
21904                 tag: 'th',
21905                 cls: 'prev',
21906                 html: '<i class="fa fa-arrow-left"/>'
21907             },
21908             {
21909                 tag: 'th',
21910                 cls: 'switch',
21911                 colspan: '5'
21912             },
21913             {
21914                 tag: 'th',
21915                 cls: 'next',
21916                 html: '<i class="fa fa-arrow-right"/>'
21917             }
21918
21919             ]
21920         }
21921         ]
21922     },
21923     
21924     content : {
21925         tag: 'tbody',
21926         cn: [
21927         {
21928             tag: 'tr',
21929             cn: [
21930             {
21931                 tag: 'td',
21932                 colspan: '7'
21933             }
21934             ]
21935         }
21936         ]
21937     },
21938     
21939     footer : {
21940         tag: 'tfoot',
21941         cn: [
21942         {
21943             tag: 'tr',
21944             cn: [
21945             {
21946                 tag: 'th',
21947                 colspan: '7',
21948                 cls: 'today'
21949             }
21950                     
21951             ]
21952         }
21953         ]
21954     },
21955     
21956     dates:{
21957         en: {
21958             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21959             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21960             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21961             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21962             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21963             today: "Today"
21964         }
21965     },
21966     
21967     modes: [
21968     {
21969         clsName: 'days',
21970         navFnc: 'Month',
21971         navStep: 1
21972     },
21973     {
21974         clsName: 'months',
21975         navFnc: 'FullYear',
21976         navStep: 1
21977     },
21978     {
21979         clsName: 'years',
21980         navFnc: 'FullYear',
21981         navStep: 10
21982     }]
21983 });
21984
21985 Roo.apply(Roo.bootstrap.DateField,  {
21986   
21987     template : {
21988         tag: 'div',
21989         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21990         cn: [
21991         {
21992             tag: 'div',
21993             cls: 'datepicker-days',
21994             cn: [
21995             {
21996                 tag: 'table',
21997                 cls: 'table-condensed',
21998                 cn:[
21999                 Roo.bootstrap.DateField.head,
22000                 {
22001                     tag: 'tbody'
22002                 },
22003                 Roo.bootstrap.DateField.footer
22004                 ]
22005             }
22006             ]
22007         },
22008         {
22009             tag: 'div',
22010             cls: 'datepicker-months',
22011             cn: [
22012             {
22013                 tag: 'table',
22014                 cls: 'table-condensed',
22015                 cn:[
22016                 Roo.bootstrap.DateField.head,
22017                 Roo.bootstrap.DateField.content,
22018                 Roo.bootstrap.DateField.footer
22019                 ]
22020             }
22021             ]
22022         },
22023         {
22024             tag: 'div',
22025             cls: 'datepicker-years',
22026             cn: [
22027             {
22028                 tag: 'table',
22029                 cls: 'table-condensed',
22030                 cn:[
22031                 Roo.bootstrap.DateField.head,
22032                 Roo.bootstrap.DateField.content,
22033                 Roo.bootstrap.DateField.footer
22034                 ]
22035             }
22036             ]
22037         }
22038         ]
22039     }
22040 });
22041
22042  
22043
22044  /*
22045  * - LGPL
22046  *
22047  * TimeField
22048  * 
22049  */
22050
22051 /**
22052  * @class Roo.bootstrap.TimeField
22053  * @extends Roo.bootstrap.Input
22054  * Bootstrap DateField class
22055  * 
22056  * 
22057  * @constructor
22058  * Create a new TimeField
22059  * @param {Object} config The config object
22060  */
22061
22062 Roo.bootstrap.TimeField = function(config){
22063     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22064     this.addEvents({
22065             /**
22066              * @event show
22067              * Fires when this field show.
22068              * @param {Roo.bootstrap.DateField} thisthis
22069              * @param {Mixed} date The date value
22070              */
22071             show : true,
22072             /**
22073              * @event show
22074              * Fires when this field hide.
22075              * @param {Roo.bootstrap.DateField} this
22076              * @param {Mixed} date The date value
22077              */
22078             hide : true,
22079             /**
22080              * @event select
22081              * Fires when select a date.
22082              * @param {Roo.bootstrap.DateField} this
22083              * @param {Mixed} date The date value
22084              */
22085             select : true
22086         });
22087 };
22088
22089 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22090     
22091     /**
22092      * @cfg {String} format
22093      * The default time format string which can be overriden for localization support.  The format must be
22094      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22095      */
22096     format : "H:i",
22097
22098     getAutoCreate : function()
22099     {
22100         this.after = '<i class="fa far fa-clock"></i>';
22101         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22102         
22103          
22104     },
22105     onRender: function(ct, position)
22106     {
22107         
22108         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22109                 
22110         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22111         
22112         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22113         
22114         this.pop = this.picker().select('>.datepicker-time',true).first();
22115         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22116         
22117         this.picker().on('mousedown', this.onMousedown, this);
22118         this.picker().on('click', this.onClick, this);
22119         
22120         this.picker().addClass('datepicker-dropdown');
22121     
22122         this.fillTime();
22123         this.update();
22124             
22125         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22126         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22127         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22128         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22129         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22130         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22131
22132     },
22133     
22134     fireKey: function(e){
22135         if (!this.picker().isVisible()){
22136             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22137                 this.show();
22138             }
22139             return;
22140         }
22141
22142         e.preventDefault();
22143         
22144         switch(e.keyCode){
22145             case 27: // escape
22146                 this.hide();
22147                 break;
22148             case 37: // left
22149             case 39: // right
22150                 this.onTogglePeriod();
22151                 break;
22152             case 38: // up
22153                 this.onIncrementMinutes();
22154                 break;
22155             case 40: // down
22156                 this.onDecrementMinutes();
22157                 break;
22158             case 13: // enter
22159             case 9: // tab
22160                 this.setTime();
22161                 break;
22162         }
22163     },
22164     
22165     onClick: function(e) {
22166         e.stopPropagation();
22167         e.preventDefault();
22168     },
22169     
22170     picker : function()
22171     {
22172         return this.pickerEl;
22173     },
22174     
22175     fillTime: function()
22176     {    
22177         var time = this.pop.select('tbody', true).first();
22178         
22179         time.dom.innerHTML = '';
22180         
22181         time.createChild({
22182             tag: 'tr',
22183             cn: [
22184                 {
22185                     tag: 'td',
22186                     cn: [
22187                         {
22188                             tag: 'a',
22189                             href: '#',
22190                             cls: 'btn',
22191                             cn: [
22192                                 {
22193                                     tag: 'i',
22194                                     cls: 'hours-up fa fas fa-chevron-up'
22195                                 }
22196                             ]
22197                         } 
22198                     ]
22199                 },
22200                 {
22201                     tag: 'td',
22202                     cls: 'separator'
22203                 },
22204                 {
22205                     tag: 'td',
22206                     cn: [
22207                         {
22208                             tag: 'a',
22209                             href: '#',
22210                             cls: 'btn',
22211                             cn: [
22212                                 {
22213                                     tag: 'i',
22214                                     cls: 'minutes-up fa fas fa-chevron-up'
22215                                 }
22216                             ]
22217                         }
22218                     ]
22219                 },
22220                 {
22221                     tag: 'td',
22222                     cls: 'separator'
22223                 }
22224             ]
22225         });
22226         
22227         time.createChild({
22228             tag: 'tr',
22229             cn: [
22230                 {
22231                     tag: 'td',
22232                     cn: [
22233                         {
22234                             tag: 'span',
22235                             cls: 'timepicker-hour',
22236                             html: '00'
22237                         }  
22238                     ]
22239                 },
22240                 {
22241                     tag: 'td',
22242                     cls: 'separator',
22243                     html: ':'
22244                 },
22245                 {
22246                     tag: 'td',
22247                     cn: [
22248                         {
22249                             tag: 'span',
22250                             cls: 'timepicker-minute',
22251                             html: '00'
22252                         }  
22253                     ]
22254                 },
22255                 {
22256                     tag: 'td',
22257                     cls: 'separator'
22258                 },
22259                 {
22260                     tag: 'td',
22261                     cn: [
22262                         {
22263                             tag: 'button',
22264                             type: 'button',
22265                             cls: 'btn btn-primary period',
22266                             html: 'AM'
22267                             
22268                         }
22269                     ]
22270                 }
22271             ]
22272         });
22273         
22274         time.createChild({
22275             tag: 'tr',
22276             cn: [
22277                 {
22278                     tag: 'td',
22279                     cn: [
22280                         {
22281                             tag: 'a',
22282                             href: '#',
22283                             cls: 'btn',
22284                             cn: [
22285                                 {
22286                                     tag: 'span',
22287                                     cls: 'hours-down fa fas fa-chevron-down'
22288                                 }
22289                             ]
22290                         }
22291                     ]
22292                 },
22293                 {
22294                     tag: 'td',
22295                     cls: 'separator'
22296                 },
22297                 {
22298                     tag: 'td',
22299                     cn: [
22300                         {
22301                             tag: 'a',
22302                             href: '#',
22303                             cls: 'btn',
22304                             cn: [
22305                                 {
22306                                     tag: 'span',
22307                                     cls: 'minutes-down fa fas fa-chevron-down'
22308                                 }
22309                             ]
22310                         }
22311                     ]
22312                 },
22313                 {
22314                     tag: 'td',
22315                     cls: 'separator'
22316                 }
22317             ]
22318         });
22319         
22320     },
22321     
22322     update: function()
22323     {
22324         
22325         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22326         
22327         this.fill();
22328     },
22329     
22330     fill: function() 
22331     {
22332         var hours = this.time.getHours();
22333         var minutes = this.time.getMinutes();
22334         var period = 'AM';
22335         
22336         if(hours > 11){
22337             period = 'PM';
22338         }
22339         
22340         if(hours == 0){
22341             hours = 12;
22342         }
22343         
22344         
22345         if(hours > 12){
22346             hours = hours - 12;
22347         }
22348         
22349         if(hours < 10){
22350             hours = '0' + hours;
22351         }
22352         
22353         if(minutes < 10){
22354             minutes = '0' + minutes;
22355         }
22356         
22357         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22358         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22359         this.pop.select('button', true).first().dom.innerHTML = period;
22360         
22361     },
22362     
22363     place: function()
22364     {   
22365         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22366         
22367         var cls = ['bottom'];
22368         
22369         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22370             cls.pop();
22371             cls.push('top');
22372         }
22373         
22374         cls.push('right');
22375         
22376         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22377             cls.pop();
22378             cls.push('left');
22379         }
22380         //this.picker().setXY(20000,20000);
22381         this.picker().addClass(cls.join('-'));
22382         
22383         var _this = this;
22384         
22385         Roo.each(cls, function(c){
22386             if(c == 'bottom'){
22387                 (function() {
22388                  //  
22389                 }).defer(200);
22390                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22391                 //_this.picker().setTop(_this.inputEl().getHeight());
22392                 return;
22393             }
22394             if(c == 'top'){
22395                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22396                 
22397                 //_this.picker().setTop(0 - _this.picker().getHeight());
22398                 return;
22399             }
22400             /*
22401             if(c == 'left'){
22402                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22403                 return;
22404             }
22405             if(c == 'right'){
22406                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22407                 return;
22408             }
22409             */
22410         });
22411         
22412     },
22413   
22414     onFocus : function()
22415     {
22416         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22417         this.show();
22418     },
22419     
22420     onBlur : function()
22421     {
22422         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22423         this.hide();
22424     },
22425     
22426     show : function()
22427     {
22428         this.picker().show();
22429         this.pop.show();
22430         this.update();
22431         this.place();
22432         
22433         this.fireEvent('show', this, this.date);
22434     },
22435     
22436     hide : function()
22437     {
22438         this.picker().hide();
22439         this.pop.hide();
22440         
22441         this.fireEvent('hide', this, this.date);
22442     },
22443     
22444     setTime : function()
22445     {
22446         this.hide();
22447         this.setValue(this.time.format(this.format));
22448         
22449         this.fireEvent('select', this, this.date);
22450         
22451         
22452     },
22453     
22454     onMousedown: function(e){
22455         e.stopPropagation();
22456         e.preventDefault();
22457     },
22458     
22459     onIncrementHours: function()
22460     {
22461         Roo.log('onIncrementHours');
22462         this.time = this.time.add(Date.HOUR, 1);
22463         this.update();
22464         
22465     },
22466     
22467     onDecrementHours: function()
22468     {
22469         Roo.log('onDecrementHours');
22470         this.time = this.time.add(Date.HOUR, -1);
22471         this.update();
22472     },
22473     
22474     onIncrementMinutes: function()
22475     {
22476         Roo.log('onIncrementMinutes');
22477         this.time = this.time.add(Date.MINUTE, 1);
22478         this.update();
22479     },
22480     
22481     onDecrementMinutes: function()
22482     {
22483         Roo.log('onDecrementMinutes');
22484         this.time = this.time.add(Date.MINUTE, -1);
22485         this.update();
22486     },
22487     
22488     onTogglePeriod: function()
22489     {
22490         Roo.log('onTogglePeriod');
22491         this.time = this.time.add(Date.HOUR, 12);
22492         this.update();
22493     }
22494     
22495    
22496 });
22497  
22498
22499 Roo.apply(Roo.bootstrap.TimeField,  {
22500   
22501     template : {
22502         tag: 'div',
22503         cls: 'datepicker dropdown-menu',
22504         cn: [
22505             {
22506                 tag: 'div',
22507                 cls: 'datepicker-time',
22508                 cn: [
22509                 {
22510                     tag: 'table',
22511                     cls: 'table-condensed',
22512                     cn:[
22513                         {
22514                             tag: 'tbody',
22515                             cn: [
22516                                 {
22517                                     tag: 'tr',
22518                                     cn: [
22519                                     {
22520                                         tag: 'td',
22521                                         colspan: '7'
22522                                     }
22523                                     ]
22524                                 }
22525                             ]
22526                         },
22527                         {
22528                             tag: 'tfoot',
22529                             cn: [
22530                                 {
22531                                     tag: 'tr',
22532                                     cn: [
22533                                     {
22534                                         tag: 'th',
22535                                         colspan: '7',
22536                                         cls: '',
22537                                         cn: [
22538                                             {
22539                                                 tag: 'button',
22540                                                 cls: 'btn btn-info ok',
22541                                                 html: 'OK'
22542                                             }
22543                                         ]
22544                                     }
22545                     
22546                                     ]
22547                                 }
22548                             ]
22549                         }
22550                     ]
22551                 }
22552                 ]
22553             }
22554         ]
22555     }
22556 });
22557
22558  
22559
22560  /*
22561  * - LGPL
22562  *
22563  * MonthField
22564  * 
22565  */
22566
22567 /**
22568  * @class Roo.bootstrap.MonthField
22569  * @extends Roo.bootstrap.Input
22570  * Bootstrap MonthField class
22571  * 
22572  * @cfg {String} language default en
22573  * 
22574  * @constructor
22575  * Create a new MonthField
22576  * @param {Object} config The config object
22577  */
22578
22579 Roo.bootstrap.MonthField = function(config){
22580     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22581     
22582     this.addEvents({
22583         /**
22584          * @event show
22585          * Fires when this field show.
22586          * @param {Roo.bootstrap.MonthField} this
22587          * @param {Mixed} date The date value
22588          */
22589         show : true,
22590         /**
22591          * @event show
22592          * Fires when this field hide.
22593          * @param {Roo.bootstrap.MonthField} this
22594          * @param {Mixed} date The date value
22595          */
22596         hide : true,
22597         /**
22598          * @event select
22599          * Fires when select a date.
22600          * @param {Roo.bootstrap.MonthField} this
22601          * @param {String} oldvalue The old value
22602          * @param {String} newvalue The new value
22603          */
22604         select : true
22605     });
22606 };
22607
22608 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22609     
22610     onRender: function(ct, position)
22611     {
22612         
22613         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22614         
22615         this.language = this.language || 'en';
22616         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22617         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22618         
22619         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22620         this.isInline = false;
22621         this.isInput = true;
22622         this.component = this.el.select('.add-on', true).first() || false;
22623         this.component = (this.component && this.component.length === 0) ? false : this.component;
22624         this.hasInput = this.component && this.inputEL().length;
22625         
22626         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22627         
22628         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22629         
22630         this.picker().on('mousedown', this.onMousedown, this);
22631         this.picker().on('click', this.onClick, this);
22632         
22633         this.picker().addClass('datepicker-dropdown');
22634         
22635         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22636             v.setStyle('width', '189px');
22637         });
22638         
22639         this.fillMonths();
22640         
22641         this.update();
22642         
22643         if(this.isInline) {
22644             this.show();
22645         }
22646         
22647     },
22648     
22649     setValue: function(v, suppressEvent)
22650     {   
22651         var o = this.getValue();
22652         
22653         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22654         
22655         this.update();
22656
22657         if(suppressEvent !== true){
22658             this.fireEvent('select', this, o, v);
22659         }
22660         
22661     },
22662     
22663     getValue: function()
22664     {
22665         return this.value;
22666     },
22667     
22668     onClick: function(e) 
22669     {
22670         e.stopPropagation();
22671         e.preventDefault();
22672         
22673         var target = e.getTarget();
22674         
22675         if(target.nodeName.toLowerCase() === 'i'){
22676             target = Roo.get(target).dom.parentNode;
22677         }
22678         
22679         var nodeName = target.nodeName;
22680         var className = target.className;
22681         var html = target.innerHTML;
22682         
22683         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22684             return;
22685         }
22686         
22687         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22688         
22689         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22690         
22691         this.hide();
22692                         
22693     },
22694     
22695     picker : function()
22696     {
22697         return this.pickerEl;
22698     },
22699     
22700     fillMonths: function()
22701     {    
22702         var i = 0;
22703         var months = this.picker().select('>.datepicker-months td', true).first();
22704         
22705         months.dom.innerHTML = '';
22706         
22707         while (i < 12) {
22708             var month = {
22709                 tag: 'span',
22710                 cls: 'month',
22711                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22712             };
22713             
22714             months.createChild(month);
22715         }
22716         
22717     },
22718     
22719     update: function()
22720     {
22721         var _this = this;
22722         
22723         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22724             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22725         }
22726         
22727         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22728             e.removeClass('active');
22729             
22730             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22731                 e.addClass('active');
22732             }
22733         })
22734     },
22735     
22736     place: function()
22737     {
22738         if(this.isInline) {
22739             return;
22740         }
22741         
22742         this.picker().removeClass(['bottom', 'top']);
22743         
22744         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22745             /*
22746              * place to the top of element!
22747              *
22748              */
22749             
22750             this.picker().addClass('top');
22751             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22752             
22753             return;
22754         }
22755         
22756         this.picker().addClass('bottom');
22757         
22758         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22759     },
22760     
22761     onFocus : function()
22762     {
22763         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22764         this.show();
22765     },
22766     
22767     onBlur : function()
22768     {
22769         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22770         
22771         var d = this.inputEl().getValue();
22772         
22773         this.setValue(d);
22774                 
22775         this.hide();
22776     },
22777     
22778     show : function()
22779     {
22780         this.picker().show();
22781         this.picker().select('>.datepicker-months', true).first().show();
22782         this.update();
22783         this.place();
22784         
22785         this.fireEvent('show', this, this.date);
22786     },
22787     
22788     hide : function()
22789     {
22790         if(this.isInline) {
22791             return;
22792         }
22793         this.picker().hide();
22794         this.fireEvent('hide', this, this.date);
22795         
22796     },
22797     
22798     onMousedown: function(e)
22799     {
22800         e.stopPropagation();
22801         e.preventDefault();
22802     },
22803     
22804     keyup: function(e)
22805     {
22806         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22807         this.update();
22808     },
22809
22810     fireKey: function(e)
22811     {
22812         if (!this.picker().isVisible()){
22813             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22814                 this.show();
22815             }
22816             return;
22817         }
22818         
22819         var dir;
22820         
22821         switch(e.keyCode){
22822             case 27: // escape
22823                 this.hide();
22824                 e.preventDefault();
22825                 break;
22826             case 37: // left
22827             case 39: // right
22828                 dir = e.keyCode == 37 ? -1 : 1;
22829                 
22830                 this.vIndex = this.vIndex + dir;
22831                 
22832                 if(this.vIndex < 0){
22833                     this.vIndex = 0;
22834                 }
22835                 
22836                 if(this.vIndex > 11){
22837                     this.vIndex = 11;
22838                 }
22839                 
22840                 if(isNaN(this.vIndex)){
22841                     this.vIndex = 0;
22842                 }
22843                 
22844                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22845                 
22846                 break;
22847             case 38: // up
22848             case 40: // down
22849                 
22850                 dir = e.keyCode == 38 ? -1 : 1;
22851                 
22852                 this.vIndex = this.vIndex + dir * 4;
22853                 
22854                 if(this.vIndex < 0){
22855                     this.vIndex = 0;
22856                 }
22857                 
22858                 if(this.vIndex > 11){
22859                     this.vIndex = 11;
22860                 }
22861                 
22862                 if(isNaN(this.vIndex)){
22863                     this.vIndex = 0;
22864                 }
22865                 
22866                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22867                 break;
22868                 
22869             case 13: // enter
22870                 
22871                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22872                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22873                 }
22874                 
22875                 this.hide();
22876                 e.preventDefault();
22877                 break;
22878             case 9: // tab
22879                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22880                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22881                 }
22882                 this.hide();
22883                 break;
22884             case 16: // shift
22885             case 17: // ctrl
22886             case 18: // alt
22887                 break;
22888             default :
22889                 this.hide();
22890                 
22891         }
22892     },
22893     
22894     remove: function() 
22895     {
22896         this.picker().remove();
22897     }
22898    
22899 });
22900
22901 Roo.apply(Roo.bootstrap.MonthField,  {
22902     
22903     content : {
22904         tag: 'tbody',
22905         cn: [
22906         {
22907             tag: 'tr',
22908             cn: [
22909             {
22910                 tag: 'td',
22911                 colspan: '7'
22912             }
22913             ]
22914         }
22915         ]
22916     },
22917     
22918     dates:{
22919         en: {
22920             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22921             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22922         }
22923     }
22924 });
22925
22926 Roo.apply(Roo.bootstrap.MonthField,  {
22927   
22928     template : {
22929         tag: 'div',
22930         cls: 'datepicker dropdown-menu roo-dynamic',
22931         cn: [
22932             {
22933                 tag: 'div',
22934                 cls: 'datepicker-months',
22935                 cn: [
22936                 {
22937                     tag: 'table',
22938                     cls: 'table-condensed',
22939                     cn:[
22940                         Roo.bootstrap.DateField.content
22941                     ]
22942                 }
22943                 ]
22944             }
22945         ]
22946     }
22947 });
22948
22949  
22950
22951  
22952  /*
22953  * - LGPL
22954  *
22955  * CheckBox
22956  * 
22957  */
22958
22959 /**
22960  * @class Roo.bootstrap.CheckBox
22961  * @extends Roo.bootstrap.Input
22962  * Bootstrap CheckBox class
22963  * 
22964  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22965  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22966  * @cfg {String} boxLabel The text that appears beside the checkbox
22967  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22968  * @cfg {Boolean} checked initnal the element
22969  * @cfg {Boolean} inline inline the element (default false)
22970  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22971  * @cfg {String} tooltip label tooltip
22972  * 
22973  * @constructor
22974  * Create a new CheckBox
22975  * @param {Object} config The config object
22976  */
22977
22978 Roo.bootstrap.CheckBox = function(config){
22979     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22980    
22981     this.addEvents({
22982         /**
22983         * @event check
22984         * Fires when the element is checked or unchecked.
22985         * @param {Roo.bootstrap.CheckBox} this This input
22986         * @param {Boolean} checked The new checked value
22987         */
22988        check : true,
22989        /**
22990         * @event click
22991         * Fires when the element is click.
22992         * @param {Roo.bootstrap.CheckBox} this This input
22993         */
22994        click : true
22995     });
22996     
22997 };
22998
22999 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23000   
23001     inputType: 'checkbox',
23002     inputValue: 1,
23003     valueOff: 0,
23004     boxLabel: false,
23005     checked: false,
23006     weight : false,
23007     inline: false,
23008     tooltip : '',
23009     
23010     // checkbox success does not make any sense really.. 
23011     invalidClass : "",
23012     validClass : "",
23013     
23014     
23015     getAutoCreate : function()
23016     {
23017         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23018         
23019         var id = Roo.id();
23020         
23021         var cfg = {};
23022         
23023         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23024         
23025         if(this.inline){
23026             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23027         }
23028         
23029         var input =  {
23030             tag: 'input',
23031             id : id,
23032             type : this.inputType,
23033             value : this.inputValue,
23034             cls : 'roo-' + this.inputType, //'form-box',
23035             placeholder : this.placeholder || ''
23036             
23037         };
23038         
23039         if(this.inputType != 'radio'){
23040             var hidden =  {
23041                 tag: 'input',
23042                 type : 'hidden',
23043                 cls : 'roo-hidden-value',
23044                 value : this.checked ? this.inputValue : this.valueOff
23045             };
23046         }
23047         
23048             
23049         if (this.weight) { // Validity check?
23050             cfg.cls += " " + this.inputType + "-" + this.weight;
23051         }
23052         
23053         if (this.disabled) {
23054             input.disabled=true;
23055         }
23056         
23057         if(this.checked){
23058             input.checked = this.checked;
23059         }
23060         
23061         if (this.name) {
23062             
23063             input.name = this.name;
23064             
23065             if(this.inputType != 'radio'){
23066                 hidden.name = this.name;
23067                 input.name = '_hidden_' + this.name;
23068             }
23069         }
23070         
23071         if (this.size) {
23072             input.cls += ' input-' + this.size;
23073         }
23074         
23075         var settings=this;
23076         
23077         ['xs','sm','md','lg'].map(function(size){
23078             if (settings[size]) {
23079                 cfg.cls += ' col-' + size + '-' + settings[size];
23080             }
23081         });
23082         
23083         var inputblock = input;
23084          
23085         if (this.before || this.after) {
23086             
23087             inputblock = {
23088                 cls : 'input-group',
23089                 cn :  [] 
23090             };
23091             
23092             if (this.before) {
23093                 inputblock.cn.push({
23094                     tag :'span',
23095                     cls : 'input-group-addon',
23096                     html : this.before
23097                 });
23098             }
23099             
23100             inputblock.cn.push(input);
23101             
23102             if(this.inputType != 'radio'){
23103                 inputblock.cn.push(hidden);
23104             }
23105             
23106             if (this.after) {
23107                 inputblock.cn.push({
23108                     tag :'span',
23109                     cls : 'input-group-addon',
23110                     html : this.after
23111                 });
23112             }
23113             
23114         }
23115         var boxLabelCfg = false;
23116         
23117         if(this.boxLabel){
23118            
23119             boxLabelCfg = {
23120                 tag: 'label',
23121                 //'for': id, // box label is handled by onclick - so no for...
23122                 cls: 'box-label',
23123                 html: this.boxLabel
23124             };
23125             if(this.tooltip){
23126                 boxLabelCfg.tooltip = this.tooltip;
23127             }
23128              
23129         }
23130         
23131         
23132         if (align ==='left' && this.fieldLabel.length) {
23133 //                Roo.log("left and has label");
23134             cfg.cn = [
23135                 {
23136                     tag: 'label',
23137                     'for' :  id,
23138                     cls : 'control-label',
23139                     html : this.fieldLabel
23140                 },
23141                 {
23142                     cls : "", 
23143                     cn: [
23144                         inputblock
23145                     ]
23146                 }
23147             ];
23148             
23149             if (boxLabelCfg) {
23150                 cfg.cn[1].cn.push(boxLabelCfg);
23151             }
23152             
23153             if(this.labelWidth > 12){
23154                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23155             }
23156             
23157             if(this.labelWidth < 13 && this.labelmd == 0){
23158                 this.labelmd = this.labelWidth;
23159             }
23160             
23161             if(this.labellg > 0){
23162                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23163                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23164             }
23165             
23166             if(this.labelmd > 0){
23167                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23168                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23169             }
23170             
23171             if(this.labelsm > 0){
23172                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23173                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23174             }
23175             
23176             if(this.labelxs > 0){
23177                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23178                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23179             }
23180             
23181         } else if ( this.fieldLabel.length) {
23182 //                Roo.log(" label");
23183                 cfg.cn = [
23184                    
23185                     {
23186                         tag: this.boxLabel ? 'span' : 'label',
23187                         'for': id,
23188                         cls: 'control-label box-input-label',
23189                         //cls : 'input-group-addon',
23190                         html : this.fieldLabel
23191                     },
23192                     
23193                     inputblock
23194                     
23195                 ];
23196                 if (boxLabelCfg) {
23197                     cfg.cn.push(boxLabelCfg);
23198                 }
23199
23200         } else {
23201             
23202 //                Roo.log(" no label && no align");
23203                 cfg.cn = [  inputblock ] ;
23204                 if (boxLabelCfg) {
23205                     cfg.cn.push(boxLabelCfg);
23206                 }
23207
23208                 
23209         }
23210         
23211        
23212         
23213         if(this.inputType != 'radio'){
23214             cfg.cn.push(hidden);
23215         }
23216         
23217         return cfg;
23218         
23219     },
23220     
23221     /**
23222      * return the real input element.
23223      */
23224     inputEl: function ()
23225     {
23226         return this.el.select('input.roo-' + this.inputType,true).first();
23227     },
23228     hiddenEl: function ()
23229     {
23230         return this.el.select('input.roo-hidden-value',true).first();
23231     },
23232     
23233     labelEl: function()
23234     {
23235         return this.el.select('label.control-label',true).first();
23236     },
23237     /* depricated... */
23238     
23239     label: function()
23240     {
23241         return this.labelEl();
23242     },
23243     
23244     boxLabelEl: function()
23245     {
23246         return this.el.select('label.box-label',true).first();
23247     },
23248     
23249     initEvents : function()
23250     {
23251 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23252         
23253         this.inputEl().on('click', this.onClick,  this);
23254         
23255         if (this.boxLabel) { 
23256             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23257         }
23258         
23259         this.startValue = this.getValue();
23260         
23261         if(this.groupId){
23262             Roo.bootstrap.CheckBox.register(this);
23263         }
23264     },
23265     
23266     onClick : function(e)
23267     {   
23268         if(this.fireEvent('click', this, e) !== false){
23269             this.setChecked(!this.checked);
23270         }
23271         
23272     },
23273     
23274     setChecked : function(state,suppressEvent)
23275     {
23276         this.startValue = this.getValue();
23277
23278         if(this.inputType == 'radio'){
23279             
23280             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23281                 e.dom.checked = false;
23282             });
23283             
23284             this.inputEl().dom.checked = true;
23285             
23286             this.inputEl().dom.value = this.inputValue;
23287             
23288             if(suppressEvent !== true){
23289                 this.fireEvent('check', this, true);
23290             }
23291             
23292             this.validate();
23293             
23294             return;
23295         }
23296         
23297         this.checked = state;
23298         
23299         this.inputEl().dom.checked = state;
23300         
23301         
23302         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23303         
23304         if(suppressEvent !== true){
23305             this.fireEvent('check', this, state);
23306         }
23307         
23308         this.validate();
23309     },
23310     
23311     getValue : function()
23312     {
23313         if(this.inputType == 'radio'){
23314             return this.getGroupValue();
23315         }
23316         
23317         return this.hiddenEl().dom.value;
23318         
23319     },
23320     
23321     getGroupValue : function()
23322     {
23323         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23324             return '';
23325         }
23326         
23327         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23328     },
23329     
23330     setValue : function(v,suppressEvent)
23331     {
23332         if(this.inputType == 'radio'){
23333             this.setGroupValue(v, suppressEvent);
23334             return;
23335         }
23336         
23337         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23338         
23339         this.validate();
23340     },
23341     
23342     setGroupValue : function(v, suppressEvent)
23343     {
23344         this.startValue = this.getValue();
23345         
23346         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23347             e.dom.checked = false;
23348             
23349             if(e.dom.value == v){
23350                 e.dom.checked = true;
23351             }
23352         });
23353         
23354         if(suppressEvent !== true){
23355             this.fireEvent('check', this, true);
23356         }
23357
23358         this.validate();
23359         
23360         return;
23361     },
23362     
23363     validate : function()
23364     {
23365         if(this.getVisibilityEl().hasClass('hidden')){
23366             return true;
23367         }
23368         
23369         if(
23370                 this.disabled || 
23371                 (this.inputType == 'radio' && this.validateRadio()) ||
23372                 (this.inputType == 'checkbox' && this.validateCheckbox())
23373         ){
23374             this.markValid();
23375             return true;
23376         }
23377         
23378         this.markInvalid();
23379         return false;
23380     },
23381     
23382     validateRadio : function()
23383     {
23384         if(this.getVisibilityEl().hasClass('hidden')){
23385             return true;
23386         }
23387         
23388         if(this.allowBlank){
23389             return true;
23390         }
23391         
23392         var valid = false;
23393         
23394         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23395             if(!e.dom.checked){
23396                 return;
23397             }
23398             
23399             valid = true;
23400             
23401             return false;
23402         });
23403         
23404         return valid;
23405     },
23406     
23407     validateCheckbox : function()
23408     {
23409         if(!this.groupId){
23410             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23411             //return (this.getValue() == this.inputValue) ? true : false;
23412         }
23413         
23414         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23415         
23416         if(!group){
23417             return false;
23418         }
23419         
23420         var r = false;
23421         
23422         for(var i in group){
23423             if(group[i].el.isVisible(true)){
23424                 r = false;
23425                 break;
23426             }
23427             
23428             r = true;
23429         }
23430         
23431         for(var i in group){
23432             if(r){
23433                 break;
23434             }
23435             
23436             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23437         }
23438         
23439         return r;
23440     },
23441     
23442     /**
23443      * Mark this field as valid
23444      */
23445     markValid : function()
23446     {
23447         var _this = this;
23448         
23449         this.fireEvent('valid', this);
23450         
23451         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23452         
23453         if(this.groupId){
23454             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23455         }
23456         
23457         if(label){
23458             label.markValid();
23459         }
23460
23461         if(this.inputType == 'radio'){
23462             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23463                 var fg = e.findParent('.form-group', false, true);
23464                 if (Roo.bootstrap.version == 3) {
23465                     fg.removeClass([_this.invalidClass, _this.validClass]);
23466                     fg.addClass(_this.validClass);
23467                 } else {
23468                     fg.removeClass(['is-valid', 'is-invalid']);
23469                     fg.addClass('is-valid');
23470                 }
23471             });
23472             
23473             return;
23474         }
23475
23476         if(!this.groupId){
23477             var fg = this.el.findParent('.form-group', false, true);
23478             if (Roo.bootstrap.version == 3) {
23479                 fg.removeClass([this.invalidClass, this.validClass]);
23480                 fg.addClass(this.validClass);
23481             } else {
23482                 fg.removeClass(['is-valid', 'is-invalid']);
23483                 fg.addClass('is-valid');
23484             }
23485             return;
23486         }
23487         
23488         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23489         
23490         if(!group){
23491             return;
23492         }
23493         
23494         for(var i in group){
23495             var fg = group[i].el.findParent('.form-group', false, true);
23496             if (Roo.bootstrap.version == 3) {
23497                 fg.removeClass([this.invalidClass, this.validClass]);
23498                 fg.addClass(this.validClass);
23499             } else {
23500                 fg.removeClass(['is-valid', 'is-invalid']);
23501                 fg.addClass('is-valid');
23502             }
23503         }
23504     },
23505     
23506      /**
23507      * Mark this field as invalid
23508      * @param {String} msg The validation message
23509      */
23510     markInvalid : function(msg)
23511     {
23512         if(this.allowBlank){
23513             return;
23514         }
23515         
23516         var _this = this;
23517         
23518         this.fireEvent('invalid', this, msg);
23519         
23520         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23521         
23522         if(this.groupId){
23523             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23524         }
23525         
23526         if(label){
23527             label.markInvalid();
23528         }
23529             
23530         if(this.inputType == 'radio'){
23531             
23532             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23533                 var fg = e.findParent('.form-group', false, true);
23534                 if (Roo.bootstrap.version == 3) {
23535                     fg.removeClass([_this.invalidClass, _this.validClass]);
23536                     fg.addClass(_this.invalidClass);
23537                 } else {
23538                     fg.removeClass(['is-invalid', 'is-valid']);
23539                     fg.addClass('is-invalid');
23540                 }
23541             });
23542             
23543             return;
23544         }
23545         
23546         if(!this.groupId){
23547             var fg = this.el.findParent('.form-group', false, true);
23548             if (Roo.bootstrap.version == 3) {
23549                 fg.removeClass([_this.invalidClass, _this.validClass]);
23550                 fg.addClass(_this.invalidClass);
23551             } else {
23552                 fg.removeClass(['is-invalid', 'is-valid']);
23553                 fg.addClass('is-invalid');
23554             }
23555             return;
23556         }
23557         
23558         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23559         
23560         if(!group){
23561             return;
23562         }
23563         
23564         for(var i in group){
23565             var fg = group[i].el.findParent('.form-group', false, true);
23566             if (Roo.bootstrap.version == 3) {
23567                 fg.removeClass([_this.invalidClass, _this.validClass]);
23568                 fg.addClass(_this.invalidClass);
23569             } else {
23570                 fg.removeClass(['is-invalid', 'is-valid']);
23571                 fg.addClass('is-invalid');
23572             }
23573         }
23574         
23575     },
23576     
23577     clearInvalid : function()
23578     {
23579         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23580         
23581         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23582         
23583         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23584         
23585         if (label && label.iconEl) {
23586             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23587             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23588         }
23589     },
23590     
23591     disable : function()
23592     {
23593         if(this.inputType != 'radio'){
23594             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23595             return;
23596         }
23597         
23598         var _this = this;
23599         
23600         if(this.rendered){
23601             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23602                 _this.getActionEl().addClass(this.disabledClass);
23603                 e.dom.disabled = true;
23604             });
23605         }
23606         
23607         this.disabled = true;
23608         this.fireEvent("disable", this);
23609         return this;
23610     },
23611
23612     enable : function()
23613     {
23614         if(this.inputType != 'radio'){
23615             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23616             return;
23617         }
23618         
23619         var _this = this;
23620         
23621         if(this.rendered){
23622             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23623                 _this.getActionEl().removeClass(this.disabledClass);
23624                 e.dom.disabled = false;
23625             });
23626         }
23627         
23628         this.disabled = false;
23629         this.fireEvent("enable", this);
23630         return this;
23631     },
23632     
23633     setBoxLabel : function(v)
23634     {
23635         this.boxLabel = v;
23636         
23637         if(this.rendered){
23638             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23639         }
23640     }
23641
23642 });
23643
23644 Roo.apply(Roo.bootstrap.CheckBox, {
23645     
23646     groups: {},
23647     
23648      /**
23649     * register a CheckBox Group
23650     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23651     */
23652     register : function(checkbox)
23653     {
23654         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23655             this.groups[checkbox.groupId] = {};
23656         }
23657         
23658         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23659             return;
23660         }
23661         
23662         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23663         
23664     },
23665     /**
23666     * fetch a CheckBox Group based on the group ID
23667     * @param {string} the group ID
23668     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23669     */
23670     get: function(groupId) {
23671         if (typeof(this.groups[groupId]) == 'undefined') {
23672             return false;
23673         }
23674         
23675         return this.groups[groupId] ;
23676     }
23677     
23678     
23679 });
23680 /*
23681  * - LGPL
23682  *
23683  * RadioItem
23684  * 
23685  */
23686
23687 /**
23688  * @class Roo.bootstrap.Radio
23689  * @extends Roo.bootstrap.Component
23690  * Bootstrap Radio class
23691  * @cfg {String} boxLabel - the label associated
23692  * @cfg {String} value - the value of radio
23693  * 
23694  * @constructor
23695  * Create a new Radio
23696  * @param {Object} config The config object
23697  */
23698 Roo.bootstrap.Radio = function(config){
23699     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23700     
23701 };
23702
23703 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23704     
23705     boxLabel : '',
23706     
23707     value : '',
23708     
23709     getAutoCreate : function()
23710     {
23711         var cfg = {
23712             tag : 'div',
23713             cls : 'form-group radio',
23714             cn : [
23715                 {
23716                     tag : 'label',
23717                     cls : 'box-label',
23718                     html : this.boxLabel
23719                 }
23720             ]
23721         };
23722         
23723         return cfg;
23724     },
23725     
23726     initEvents : function() 
23727     {
23728         this.parent().register(this);
23729         
23730         this.el.on('click', this.onClick, this);
23731         
23732     },
23733     
23734     onClick : function(e)
23735     {
23736         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23737             this.setChecked(true);
23738         }
23739     },
23740     
23741     setChecked : function(state, suppressEvent)
23742     {
23743         this.parent().setValue(this.value, suppressEvent);
23744         
23745     },
23746     
23747     setBoxLabel : function(v)
23748     {
23749         this.boxLabel = v;
23750         
23751         if(this.rendered){
23752             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23753         }
23754     }
23755     
23756 });
23757  
23758
23759  /*
23760  * - LGPL
23761  *
23762  * Input
23763  * 
23764  */
23765
23766 /**
23767  * @class Roo.bootstrap.SecurePass
23768  * @extends Roo.bootstrap.Input
23769  * Bootstrap SecurePass class
23770  *
23771  * 
23772  * @constructor
23773  * Create a new SecurePass
23774  * @param {Object} config The config object
23775  */
23776  
23777 Roo.bootstrap.SecurePass = function (config) {
23778     // these go here, so the translation tool can replace them..
23779     this.errors = {
23780         PwdEmpty: "Please type a password, and then retype it to confirm.",
23781         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23782         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23783         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23784         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23785         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23786         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23787         TooWeak: "Your password is Too Weak."
23788     },
23789     this.meterLabel = "Password strength:";
23790     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23791     this.meterClass = [
23792         "roo-password-meter-tooweak", 
23793         "roo-password-meter-weak", 
23794         "roo-password-meter-medium", 
23795         "roo-password-meter-strong", 
23796         "roo-password-meter-grey"
23797     ];
23798     
23799     this.errors = {};
23800     
23801     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23802 }
23803
23804 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23805     /**
23806      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23807      * {
23808      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23809      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23810      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23811      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23812      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23813      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23814      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23815      * })
23816      */
23817     // private
23818     
23819     meterWidth: 300,
23820     errorMsg :'',    
23821     errors: false,
23822     imageRoot: '/',
23823     /**
23824      * @cfg {String/Object} Label for the strength meter (defaults to
23825      * 'Password strength:')
23826      */
23827     // private
23828     meterLabel: '',
23829     /**
23830      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23831      * ['Weak', 'Medium', 'Strong'])
23832      */
23833     // private    
23834     pwdStrengths: false,    
23835     // private
23836     strength: 0,
23837     // private
23838     _lastPwd: null,
23839     // private
23840     kCapitalLetter: 0,
23841     kSmallLetter: 1,
23842     kDigit: 2,
23843     kPunctuation: 3,
23844     
23845     insecure: false,
23846     // private
23847     initEvents: function ()
23848     {
23849         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23850
23851         if (this.el.is('input[type=password]') && Roo.isSafari) {
23852             this.el.on('keydown', this.SafariOnKeyDown, this);
23853         }
23854
23855         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23856     },
23857     // private
23858     onRender: function (ct, position)
23859     {
23860         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23861         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23862         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23863
23864         this.trigger.createChild({
23865                    cn: [
23866                     {
23867                     //id: 'PwdMeter',
23868                     tag: 'div',
23869                     cls: 'roo-password-meter-grey col-xs-12',
23870                     style: {
23871                         //width: 0,
23872                         //width: this.meterWidth + 'px'                                                
23873                         }
23874                     },
23875                     {                            
23876                          cls: 'roo-password-meter-text'                          
23877                     }
23878                 ]            
23879         });
23880
23881          
23882         if (this.hideTrigger) {
23883             this.trigger.setDisplayed(false);
23884         }
23885         this.setSize(this.width || '', this.height || '');
23886     },
23887     // private
23888     onDestroy: function ()
23889     {
23890         if (this.trigger) {
23891             this.trigger.removeAllListeners();
23892             this.trigger.remove();
23893         }
23894         if (this.wrap) {
23895             this.wrap.remove();
23896         }
23897         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23898     },
23899     // private
23900     checkStrength: function ()
23901     {
23902         var pwd = this.inputEl().getValue();
23903         if (pwd == this._lastPwd) {
23904             return;
23905         }
23906
23907         var strength;
23908         if (this.ClientSideStrongPassword(pwd)) {
23909             strength = 3;
23910         } else if (this.ClientSideMediumPassword(pwd)) {
23911             strength = 2;
23912         } else if (this.ClientSideWeakPassword(pwd)) {
23913             strength = 1;
23914         } else {
23915             strength = 0;
23916         }
23917         
23918         Roo.log('strength1: ' + strength);
23919         
23920         //var pm = this.trigger.child('div/div/div').dom;
23921         var pm = this.trigger.child('div/div');
23922         pm.removeClass(this.meterClass);
23923         pm.addClass(this.meterClass[strength]);
23924                 
23925         
23926         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23927                 
23928         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23929         
23930         this._lastPwd = pwd;
23931     },
23932     reset: function ()
23933     {
23934         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23935         
23936         this._lastPwd = '';
23937         
23938         var pm = this.trigger.child('div/div');
23939         pm.removeClass(this.meterClass);
23940         pm.addClass('roo-password-meter-grey');        
23941         
23942         
23943         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23944         
23945         pt.innerHTML = '';
23946         this.inputEl().dom.type='password';
23947     },
23948     // private
23949     validateValue: function (value)
23950     {
23951         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23952             return false;
23953         }
23954         if (value.length == 0) {
23955             if (this.allowBlank) {
23956                 this.clearInvalid();
23957                 return true;
23958             }
23959
23960             this.markInvalid(this.errors.PwdEmpty);
23961             this.errorMsg = this.errors.PwdEmpty;
23962             return false;
23963         }
23964         
23965         if(this.insecure){
23966             return true;
23967         }
23968         
23969         if (!value.match(/[\x21-\x7e]+/)) {
23970             this.markInvalid(this.errors.PwdBadChar);
23971             this.errorMsg = this.errors.PwdBadChar;
23972             return false;
23973         }
23974         if (value.length < 6) {
23975             this.markInvalid(this.errors.PwdShort);
23976             this.errorMsg = this.errors.PwdShort;
23977             return false;
23978         }
23979         if (value.length > 16) {
23980             this.markInvalid(this.errors.PwdLong);
23981             this.errorMsg = this.errors.PwdLong;
23982             return false;
23983         }
23984         var strength;
23985         if (this.ClientSideStrongPassword(value)) {
23986             strength = 3;
23987         } else if (this.ClientSideMediumPassword(value)) {
23988             strength = 2;
23989         } else if (this.ClientSideWeakPassword(value)) {
23990             strength = 1;
23991         } else {
23992             strength = 0;
23993         }
23994
23995         
23996         if (strength < 2) {
23997             //this.markInvalid(this.errors.TooWeak);
23998             this.errorMsg = this.errors.TooWeak;
23999             //return false;
24000         }
24001         
24002         
24003         console.log('strength2: ' + strength);
24004         
24005         //var pm = this.trigger.child('div/div/div').dom;
24006         
24007         var pm = this.trigger.child('div/div');
24008         pm.removeClass(this.meterClass);
24009         pm.addClass(this.meterClass[strength]);
24010                 
24011         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24012                 
24013         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24014         
24015         this.errorMsg = ''; 
24016         return true;
24017     },
24018     // private
24019     CharacterSetChecks: function (type)
24020     {
24021         this.type = type;
24022         this.fResult = false;
24023     },
24024     // private
24025     isctype: function (character, type)
24026     {
24027         switch (type) {  
24028             case this.kCapitalLetter:
24029                 if (character >= 'A' && character <= 'Z') {
24030                     return true;
24031                 }
24032                 break;
24033             
24034             case this.kSmallLetter:
24035                 if (character >= 'a' && character <= 'z') {
24036                     return true;
24037                 }
24038                 break;
24039             
24040             case this.kDigit:
24041                 if (character >= '0' && character <= '9') {
24042                     return true;
24043                 }
24044                 break;
24045             
24046             case this.kPunctuation:
24047                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24048                     return true;
24049                 }
24050                 break;
24051             
24052             default:
24053                 return false;
24054         }
24055
24056     },
24057     // private
24058     IsLongEnough: function (pwd, size)
24059     {
24060         return !(pwd == null || isNaN(size) || pwd.length < size);
24061     },
24062     // private
24063     SpansEnoughCharacterSets: function (word, nb)
24064     {
24065         if (!this.IsLongEnough(word, nb))
24066         {
24067             return false;
24068         }
24069
24070         var characterSetChecks = new Array(
24071             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24072             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24073         );
24074         
24075         for (var index = 0; index < word.length; ++index) {
24076             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24077                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24078                     characterSetChecks[nCharSet].fResult = true;
24079                     break;
24080                 }
24081             }
24082         }
24083
24084         var nCharSets = 0;
24085         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24086             if (characterSetChecks[nCharSet].fResult) {
24087                 ++nCharSets;
24088             }
24089         }
24090
24091         if (nCharSets < nb) {
24092             return false;
24093         }
24094         return true;
24095     },
24096     // private
24097     ClientSideStrongPassword: function (pwd)
24098     {
24099         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24100     },
24101     // private
24102     ClientSideMediumPassword: function (pwd)
24103     {
24104         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24105     },
24106     // private
24107     ClientSideWeakPassword: function (pwd)
24108     {
24109         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24110     }
24111           
24112 })//<script type="text/javascript">
24113
24114 /*
24115  * Based  Ext JS Library 1.1.1
24116  * Copyright(c) 2006-2007, Ext JS, LLC.
24117  * LGPL
24118  *
24119  */
24120  
24121 /**
24122  * @class Roo.HtmlEditorCore
24123  * @extends Roo.Component
24124  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24125  *
24126  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24127  */
24128
24129 Roo.HtmlEditorCore = function(config){
24130     
24131     
24132     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24133     
24134     
24135     this.addEvents({
24136         /**
24137          * @event initialize
24138          * Fires when the editor is fully initialized (including the iframe)
24139          * @param {Roo.HtmlEditorCore} this
24140          */
24141         initialize: true,
24142         /**
24143          * @event activate
24144          * Fires when the editor is first receives the focus. Any insertion must wait
24145          * until after this event.
24146          * @param {Roo.HtmlEditorCore} this
24147          */
24148         activate: true,
24149          /**
24150          * @event beforesync
24151          * Fires before the textarea is updated with content from the editor iframe. Return false
24152          * to cancel the sync.
24153          * @param {Roo.HtmlEditorCore} this
24154          * @param {String} html
24155          */
24156         beforesync: true,
24157          /**
24158          * @event beforepush
24159          * Fires before the iframe editor is updated with content from the textarea. Return false
24160          * to cancel the push.
24161          * @param {Roo.HtmlEditorCore} this
24162          * @param {String} html
24163          */
24164         beforepush: true,
24165          /**
24166          * @event sync
24167          * Fires when the textarea is updated with content from the editor iframe.
24168          * @param {Roo.HtmlEditorCore} this
24169          * @param {String} html
24170          */
24171         sync: true,
24172          /**
24173          * @event push
24174          * Fires when the iframe editor is updated with content from the textarea.
24175          * @param {Roo.HtmlEditorCore} this
24176          * @param {String} html
24177          */
24178         push: true,
24179         
24180         /**
24181          * @event editorevent
24182          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24183          * @param {Roo.HtmlEditorCore} this
24184          */
24185         editorevent: true
24186         
24187     });
24188     
24189     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24190     
24191     // defaults : white / black...
24192     this.applyBlacklists();
24193     
24194     
24195     
24196 };
24197
24198
24199 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24200
24201
24202      /**
24203      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24204      */
24205     
24206     owner : false,
24207     
24208      /**
24209      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24210      *                        Roo.resizable.
24211      */
24212     resizable : false,
24213      /**
24214      * @cfg {Number} height (in pixels)
24215      */   
24216     height: 300,
24217    /**
24218      * @cfg {Number} width (in pixels)
24219      */   
24220     width: 500,
24221     
24222     /**
24223      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24224      * 
24225      */
24226     stylesheets: false,
24227     
24228     // id of frame..
24229     frameId: false,
24230     
24231     // private properties
24232     validationEvent : false,
24233     deferHeight: true,
24234     initialized : false,
24235     activated : false,
24236     sourceEditMode : false,
24237     onFocus : Roo.emptyFn,
24238     iframePad:3,
24239     hideMode:'offsets',
24240     
24241     clearUp: true,
24242     
24243     // blacklist + whitelisted elements..
24244     black: false,
24245     white: false,
24246      
24247     bodyCls : '',
24248
24249     /**
24250      * Protected method that will not generally be called directly. It
24251      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24252      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24253      */
24254     getDocMarkup : function(){
24255         // body styles..
24256         var st = '';
24257         
24258         // inherit styels from page...?? 
24259         if (this.stylesheets === false) {
24260             
24261             Roo.get(document.head).select('style').each(function(node) {
24262                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24263             });
24264             
24265             Roo.get(document.head).select('link').each(function(node) { 
24266                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24267             });
24268             
24269         } else if (!this.stylesheets.length) {
24270                 // simple..
24271                 st = '<style type="text/css">' +
24272                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24273                    '</style>';
24274         } else {
24275             for (var i in this.stylesheets) { 
24276                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24277             }
24278             
24279         }
24280         
24281         st +=  '<style type="text/css">' +
24282             'IMG { cursor: pointer } ' +
24283         '</style>';
24284
24285         var cls = 'roo-htmleditor-body';
24286         
24287         if(this.bodyCls.length){
24288             cls += ' ' + this.bodyCls;
24289         }
24290         
24291         return '<html><head>' + st  +
24292             //<style type="text/css">' +
24293             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24294             //'</style>' +
24295             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24296     },
24297
24298     // private
24299     onRender : function(ct, position)
24300     {
24301         var _t = this;
24302         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24303         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24304         
24305         
24306         this.el.dom.style.border = '0 none';
24307         this.el.dom.setAttribute('tabIndex', -1);
24308         this.el.addClass('x-hidden hide');
24309         
24310         
24311         
24312         if(Roo.isIE){ // fix IE 1px bogus margin
24313             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24314         }
24315        
24316         
24317         this.frameId = Roo.id();
24318         
24319          
24320         
24321         var iframe = this.owner.wrap.createChild({
24322             tag: 'iframe',
24323             cls: 'form-control', // bootstrap..
24324             id: this.frameId,
24325             name: this.frameId,
24326             frameBorder : 'no',
24327             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24328         }, this.el
24329         );
24330         
24331         
24332         this.iframe = iframe.dom;
24333
24334          this.assignDocWin();
24335         
24336         this.doc.designMode = 'on';
24337        
24338         this.doc.open();
24339         this.doc.write(this.getDocMarkup());
24340         this.doc.close();
24341
24342         
24343         var task = { // must defer to wait for browser to be ready
24344             run : function(){
24345                 //console.log("run task?" + this.doc.readyState);
24346                 this.assignDocWin();
24347                 if(this.doc.body || this.doc.readyState == 'complete'){
24348                     try {
24349                         this.doc.designMode="on";
24350                     } catch (e) {
24351                         return;
24352                     }
24353                     Roo.TaskMgr.stop(task);
24354                     this.initEditor.defer(10, this);
24355                 }
24356             },
24357             interval : 10,
24358             duration: 10000,
24359             scope: this
24360         };
24361         Roo.TaskMgr.start(task);
24362
24363     },
24364
24365     // private
24366     onResize : function(w, h)
24367     {
24368          Roo.log('resize: ' +w + ',' + h );
24369         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24370         if(!this.iframe){
24371             return;
24372         }
24373         if(typeof w == 'number'){
24374             
24375             this.iframe.style.width = w + 'px';
24376         }
24377         if(typeof h == 'number'){
24378             
24379             this.iframe.style.height = h + 'px';
24380             if(this.doc){
24381                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24382             }
24383         }
24384         
24385     },
24386
24387     /**
24388      * Toggles the editor between standard and source edit mode.
24389      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24390      */
24391     toggleSourceEdit : function(sourceEditMode){
24392         
24393         this.sourceEditMode = sourceEditMode === true;
24394         
24395         if(this.sourceEditMode){
24396  
24397             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24398             
24399         }else{
24400             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24401             //this.iframe.className = '';
24402             this.deferFocus();
24403         }
24404         //this.setSize(this.owner.wrap.getSize());
24405         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24406     },
24407
24408     
24409   
24410
24411     /**
24412      * Protected method that will not generally be called directly. If you need/want
24413      * custom HTML cleanup, this is the method you should override.
24414      * @param {String} html The HTML to be cleaned
24415      * return {String} The cleaned HTML
24416      */
24417     cleanHtml : function(html){
24418         html = String(html);
24419         if(html.length > 5){
24420             if(Roo.isSafari){ // strip safari nonsense
24421                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24422             }
24423         }
24424         if(html == '&nbsp;'){
24425             html = '';
24426         }
24427         return html;
24428     },
24429
24430     /**
24431      * HTML Editor -> Textarea
24432      * Protected method that will not generally be called directly. Syncs the contents
24433      * of the editor iframe with the textarea.
24434      */
24435     syncValue : function(){
24436         if(this.initialized){
24437             var bd = (this.doc.body || this.doc.documentElement);
24438             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24439             var html = bd.innerHTML;
24440             if(Roo.isSafari){
24441                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24442                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24443                 if(m && m[1]){
24444                     html = '<div style="'+m[0]+'">' + html + '</div>';
24445                 }
24446             }
24447             html = this.cleanHtml(html);
24448             // fix up the special chars.. normaly like back quotes in word...
24449             // however we do not want to do this with chinese..
24450             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24451                 
24452                 var cc = match.charCodeAt();
24453
24454                 // Get the character value, handling surrogate pairs
24455                 if (match.length == 2) {
24456                     // It's a surrogate pair, calculate the Unicode code point
24457                     var high = match.charCodeAt(0) - 0xD800;
24458                     var low  = match.charCodeAt(1) - 0xDC00;
24459                     cc = (high * 0x400) + low + 0x10000;
24460                 }  else if (
24461                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24462                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24463                     (cc >= 0xf900 && cc < 0xfb00 )
24464                 ) {
24465                         return match;
24466                 }  
24467          
24468                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24469                 return "&#" + cc + ";";
24470                 
24471                 
24472             });
24473             
24474             
24475              
24476             if(this.owner.fireEvent('beforesync', this, html) !== false){
24477                 this.el.dom.value = html;
24478                 this.owner.fireEvent('sync', this, html);
24479             }
24480         }
24481     },
24482
24483     /**
24484      * Protected method that will not generally be called directly. Pushes the value of the textarea
24485      * into the iframe editor.
24486      */
24487     pushValue : function(){
24488         if(this.initialized){
24489             var v = this.el.dom.value.trim();
24490             
24491 //            if(v.length < 1){
24492 //                v = '&#160;';
24493 //            }
24494             
24495             if(this.owner.fireEvent('beforepush', this, v) !== false){
24496                 var d = (this.doc.body || this.doc.documentElement);
24497                 d.innerHTML = v;
24498                 this.cleanUpPaste();
24499                 this.el.dom.value = d.innerHTML;
24500                 this.owner.fireEvent('push', this, v);
24501             }
24502         }
24503     },
24504
24505     // private
24506     deferFocus : function(){
24507         this.focus.defer(10, this);
24508     },
24509
24510     // doc'ed in Field
24511     focus : function(){
24512         if(this.win && !this.sourceEditMode){
24513             this.win.focus();
24514         }else{
24515             this.el.focus();
24516         }
24517     },
24518     
24519     assignDocWin: function()
24520     {
24521         var iframe = this.iframe;
24522         
24523          if(Roo.isIE){
24524             this.doc = iframe.contentWindow.document;
24525             this.win = iframe.contentWindow;
24526         } else {
24527 //            if (!Roo.get(this.frameId)) {
24528 //                return;
24529 //            }
24530 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24531 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24532             
24533             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24534                 return;
24535             }
24536             
24537             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24538             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24539         }
24540     },
24541     
24542     // private
24543     initEditor : function(){
24544         //console.log("INIT EDITOR");
24545         this.assignDocWin();
24546         
24547         
24548         
24549         this.doc.designMode="on";
24550         this.doc.open();
24551         this.doc.write(this.getDocMarkup());
24552         this.doc.close();
24553         
24554         var dbody = (this.doc.body || this.doc.documentElement);
24555         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24556         // this copies styles from the containing element into thsi one..
24557         // not sure why we need all of this..
24558         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24559         
24560         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24561         //ss['background-attachment'] = 'fixed'; // w3c
24562         dbody.bgProperties = 'fixed'; // ie
24563         //Roo.DomHelper.applyStyles(dbody, ss);
24564         Roo.EventManager.on(this.doc, {
24565             //'mousedown': this.onEditorEvent,
24566             'mouseup': this.onEditorEvent,
24567             'dblclick': this.onEditorEvent,
24568             'click': this.onEditorEvent,
24569             'keyup': this.onEditorEvent,
24570             buffer:100,
24571             scope: this
24572         });
24573         if(Roo.isGecko){
24574             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24575         }
24576         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24577             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24578         }
24579         this.initialized = true;
24580
24581         this.owner.fireEvent('initialize', this);
24582         this.pushValue();
24583     },
24584
24585     // private
24586     onDestroy : function(){
24587         
24588         
24589         
24590         if(this.rendered){
24591             
24592             //for (var i =0; i < this.toolbars.length;i++) {
24593             //    // fixme - ask toolbars for heights?
24594             //    this.toolbars[i].onDestroy();
24595            // }
24596             
24597             //this.wrap.dom.innerHTML = '';
24598             //this.wrap.remove();
24599         }
24600     },
24601
24602     // private
24603     onFirstFocus : function(){
24604         
24605         this.assignDocWin();
24606         
24607         
24608         this.activated = true;
24609          
24610     
24611         if(Roo.isGecko){ // prevent silly gecko errors
24612             this.win.focus();
24613             var s = this.win.getSelection();
24614             if(!s.focusNode || s.focusNode.nodeType != 3){
24615                 var r = s.getRangeAt(0);
24616                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24617                 r.collapse(true);
24618                 this.deferFocus();
24619             }
24620             try{
24621                 this.execCmd('useCSS', true);
24622                 this.execCmd('styleWithCSS', false);
24623             }catch(e){}
24624         }
24625         this.owner.fireEvent('activate', this);
24626     },
24627
24628     // private
24629     adjustFont: function(btn){
24630         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24631         //if(Roo.isSafari){ // safari
24632         //    adjust *= 2;
24633        // }
24634         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24635         if(Roo.isSafari){ // safari
24636             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24637             v =  (v < 10) ? 10 : v;
24638             v =  (v > 48) ? 48 : v;
24639             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24640             
24641         }
24642         
24643         
24644         v = Math.max(1, v+adjust);
24645         
24646         this.execCmd('FontSize', v  );
24647     },
24648
24649     onEditorEvent : function(e)
24650     {
24651         this.owner.fireEvent('editorevent', this, e);
24652       //  this.updateToolbar();
24653         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24654     },
24655
24656     insertTag : function(tg)
24657     {
24658         // could be a bit smarter... -> wrap the current selected tRoo..
24659         if (tg.toLowerCase() == 'span' ||
24660             tg.toLowerCase() == 'code' ||
24661             tg.toLowerCase() == 'sup' ||
24662             tg.toLowerCase() == 'sub' 
24663             ) {
24664             
24665             range = this.createRange(this.getSelection());
24666             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24667             wrappingNode.appendChild(range.extractContents());
24668             range.insertNode(wrappingNode);
24669
24670             return;
24671             
24672             
24673             
24674         }
24675         this.execCmd("formatblock",   tg);
24676         
24677     },
24678     
24679     insertText : function(txt)
24680     {
24681         
24682         
24683         var range = this.createRange();
24684         range.deleteContents();
24685                //alert(Sender.getAttribute('label'));
24686                
24687         range.insertNode(this.doc.createTextNode(txt));
24688     } ,
24689     
24690      
24691
24692     /**
24693      * Executes a Midas editor command on the editor document and performs necessary focus and
24694      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24695      * @param {String} cmd The Midas command
24696      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24697      */
24698     relayCmd : function(cmd, value){
24699         this.win.focus();
24700         this.execCmd(cmd, value);
24701         this.owner.fireEvent('editorevent', this);
24702         //this.updateToolbar();
24703         this.owner.deferFocus();
24704     },
24705
24706     /**
24707      * Executes a Midas editor command directly on the editor document.
24708      * For visual commands, you should use {@link #relayCmd} instead.
24709      * <b>This should only be called after the editor is initialized.</b>
24710      * @param {String} cmd The Midas command
24711      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24712      */
24713     execCmd : function(cmd, value){
24714         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24715         this.syncValue();
24716     },
24717  
24718  
24719    
24720     /**
24721      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24722      * to insert tRoo.
24723      * @param {String} text | dom node.. 
24724      */
24725     insertAtCursor : function(text)
24726     {
24727         
24728         if(!this.activated){
24729             return;
24730         }
24731         /*
24732         if(Roo.isIE){
24733             this.win.focus();
24734             var r = this.doc.selection.createRange();
24735             if(r){
24736                 r.collapse(true);
24737                 r.pasteHTML(text);
24738                 this.syncValue();
24739                 this.deferFocus();
24740             
24741             }
24742             return;
24743         }
24744         */
24745         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24746             this.win.focus();
24747             
24748             
24749             // from jquery ui (MIT licenced)
24750             var range, node;
24751             var win = this.win;
24752             
24753             if (win.getSelection && win.getSelection().getRangeAt) {
24754                 range = win.getSelection().getRangeAt(0);
24755                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24756                 range.insertNode(node);
24757             } else if (win.document.selection && win.document.selection.createRange) {
24758                 // no firefox support
24759                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24760                 win.document.selection.createRange().pasteHTML(txt);
24761             } else {
24762                 // no firefox support
24763                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24764                 this.execCmd('InsertHTML', txt);
24765             } 
24766             
24767             this.syncValue();
24768             
24769             this.deferFocus();
24770         }
24771     },
24772  // private
24773     mozKeyPress : function(e){
24774         if(e.ctrlKey){
24775             var c = e.getCharCode(), cmd;
24776           
24777             if(c > 0){
24778                 c = String.fromCharCode(c).toLowerCase();
24779                 switch(c){
24780                     case 'b':
24781                         cmd = 'bold';
24782                         break;
24783                     case 'i':
24784                         cmd = 'italic';
24785                         break;
24786                     
24787                     case 'u':
24788                         cmd = 'underline';
24789                         break;
24790                     
24791                     case 'v':
24792                         this.cleanUpPaste.defer(100, this);
24793                         return;
24794                         
24795                 }
24796                 if(cmd){
24797                     this.win.focus();
24798                     this.execCmd(cmd);
24799                     this.deferFocus();
24800                     e.preventDefault();
24801                 }
24802                 
24803             }
24804         }
24805     },
24806
24807     // private
24808     fixKeys : function(){ // load time branching for fastest keydown performance
24809         if(Roo.isIE){
24810             return function(e){
24811                 var k = e.getKey(), r;
24812                 if(k == e.TAB){
24813                     e.stopEvent();
24814                     r = this.doc.selection.createRange();
24815                     if(r){
24816                         r.collapse(true);
24817                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24818                         this.deferFocus();
24819                     }
24820                     return;
24821                 }
24822                 
24823                 if(k == e.ENTER){
24824                     r = this.doc.selection.createRange();
24825                     if(r){
24826                         var target = r.parentElement();
24827                         if(!target || target.tagName.toLowerCase() != 'li'){
24828                             e.stopEvent();
24829                             r.pasteHTML('<br />');
24830                             r.collapse(false);
24831                             r.select();
24832                         }
24833                     }
24834                 }
24835                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24836                     this.cleanUpPaste.defer(100, this);
24837                     return;
24838                 }
24839                 
24840                 
24841             };
24842         }else if(Roo.isOpera){
24843             return function(e){
24844                 var k = e.getKey();
24845                 if(k == e.TAB){
24846                     e.stopEvent();
24847                     this.win.focus();
24848                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24849                     this.deferFocus();
24850                 }
24851                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24852                     this.cleanUpPaste.defer(100, this);
24853                     return;
24854                 }
24855                 
24856             };
24857         }else if(Roo.isSafari){
24858             return function(e){
24859                 var k = e.getKey();
24860                 
24861                 if(k == e.TAB){
24862                     e.stopEvent();
24863                     this.execCmd('InsertText','\t');
24864                     this.deferFocus();
24865                     return;
24866                 }
24867                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24868                     this.cleanUpPaste.defer(100, this);
24869                     return;
24870                 }
24871                 
24872              };
24873         }
24874     }(),
24875     
24876     getAllAncestors: function()
24877     {
24878         var p = this.getSelectedNode();
24879         var a = [];
24880         if (!p) {
24881             a.push(p); // push blank onto stack..
24882             p = this.getParentElement();
24883         }
24884         
24885         
24886         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24887             a.push(p);
24888             p = p.parentNode;
24889         }
24890         a.push(this.doc.body);
24891         return a;
24892     },
24893     lastSel : false,
24894     lastSelNode : false,
24895     
24896     
24897     getSelection : function() 
24898     {
24899         this.assignDocWin();
24900         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24901     },
24902     
24903     getSelectedNode: function() 
24904     {
24905         // this may only work on Gecko!!!
24906         
24907         // should we cache this!!!!
24908         
24909         
24910         
24911          
24912         var range = this.createRange(this.getSelection()).cloneRange();
24913         
24914         if (Roo.isIE) {
24915             var parent = range.parentElement();
24916             while (true) {
24917                 var testRange = range.duplicate();
24918                 testRange.moveToElementText(parent);
24919                 if (testRange.inRange(range)) {
24920                     break;
24921                 }
24922                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24923                     break;
24924                 }
24925                 parent = parent.parentElement;
24926             }
24927             return parent;
24928         }
24929         
24930         // is ancestor a text element.
24931         var ac =  range.commonAncestorContainer;
24932         if (ac.nodeType == 3) {
24933             ac = ac.parentNode;
24934         }
24935         
24936         var ar = ac.childNodes;
24937          
24938         var nodes = [];
24939         var other_nodes = [];
24940         var has_other_nodes = false;
24941         for (var i=0;i<ar.length;i++) {
24942             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24943                 continue;
24944             }
24945             // fullly contained node.
24946             
24947             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24948                 nodes.push(ar[i]);
24949                 continue;
24950             }
24951             
24952             // probably selected..
24953             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24954                 other_nodes.push(ar[i]);
24955                 continue;
24956             }
24957             // outer..
24958             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24959                 continue;
24960             }
24961             
24962             
24963             has_other_nodes = true;
24964         }
24965         if (!nodes.length && other_nodes.length) {
24966             nodes= other_nodes;
24967         }
24968         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24969             return false;
24970         }
24971         
24972         return nodes[0];
24973     },
24974     createRange: function(sel)
24975     {
24976         // this has strange effects when using with 
24977         // top toolbar - not sure if it's a great idea.
24978         //this.editor.contentWindow.focus();
24979         if (typeof sel != "undefined") {
24980             try {
24981                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24982             } catch(e) {
24983                 return this.doc.createRange();
24984             }
24985         } else {
24986             return this.doc.createRange();
24987         }
24988     },
24989     getParentElement: function()
24990     {
24991         
24992         this.assignDocWin();
24993         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24994         
24995         var range = this.createRange(sel);
24996          
24997         try {
24998             var p = range.commonAncestorContainer;
24999             while (p.nodeType == 3) { // text node
25000                 p = p.parentNode;
25001             }
25002             return p;
25003         } catch (e) {
25004             return null;
25005         }
25006     
25007     },
25008     /***
25009      *
25010      * Range intersection.. the hard stuff...
25011      *  '-1' = before
25012      *  '0' = hits..
25013      *  '1' = after.
25014      *         [ -- selected range --- ]
25015      *   [fail]                        [fail]
25016      *
25017      *    basically..
25018      *      if end is before start or  hits it. fail.
25019      *      if start is after end or hits it fail.
25020      *
25021      *   if either hits (but other is outside. - then it's not 
25022      *   
25023      *    
25024      **/
25025     
25026     
25027     // @see http://www.thismuchiknow.co.uk/?p=64.
25028     rangeIntersectsNode : function(range, node)
25029     {
25030         var nodeRange = node.ownerDocument.createRange();
25031         try {
25032             nodeRange.selectNode(node);
25033         } catch (e) {
25034             nodeRange.selectNodeContents(node);
25035         }
25036     
25037         var rangeStartRange = range.cloneRange();
25038         rangeStartRange.collapse(true);
25039     
25040         var rangeEndRange = range.cloneRange();
25041         rangeEndRange.collapse(false);
25042     
25043         var nodeStartRange = nodeRange.cloneRange();
25044         nodeStartRange.collapse(true);
25045     
25046         var nodeEndRange = nodeRange.cloneRange();
25047         nodeEndRange.collapse(false);
25048     
25049         return rangeStartRange.compareBoundaryPoints(
25050                  Range.START_TO_START, nodeEndRange) == -1 &&
25051                rangeEndRange.compareBoundaryPoints(
25052                  Range.START_TO_START, nodeStartRange) == 1;
25053         
25054          
25055     },
25056     rangeCompareNode : function(range, node)
25057     {
25058         var nodeRange = node.ownerDocument.createRange();
25059         try {
25060             nodeRange.selectNode(node);
25061         } catch (e) {
25062             nodeRange.selectNodeContents(node);
25063         }
25064         
25065         
25066         range.collapse(true);
25067     
25068         nodeRange.collapse(true);
25069      
25070         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25071         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25072          
25073         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25074         
25075         var nodeIsBefore   =  ss == 1;
25076         var nodeIsAfter    = ee == -1;
25077         
25078         if (nodeIsBefore && nodeIsAfter) {
25079             return 0; // outer
25080         }
25081         if (!nodeIsBefore && nodeIsAfter) {
25082             return 1; //right trailed.
25083         }
25084         
25085         if (nodeIsBefore && !nodeIsAfter) {
25086             return 2;  // left trailed.
25087         }
25088         // fully contined.
25089         return 3;
25090     },
25091
25092     // private? - in a new class?
25093     cleanUpPaste :  function()
25094     {
25095         // cleans up the whole document..
25096         Roo.log('cleanuppaste');
25097         
25098         this.cleanUpChildren(this.doc.body);
25099         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25100         if (clean != this.doc.body.innerHTML) {
25101             this.doc.body.innerHTML = clean;
25102         }
25103         
25104     },
25105     
25106     cleanWordChars : function(input) {// change the chars to hex code
25107         var he = Roo.HtmlEditorCore;
25108         
25109         var output = input;
25110         Roo.each(he.swapCodes, function(sw) { 
25111             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25112             
25113             output = output.replace(swapper, sw[1]);
25114         });
25115         
25116         return output;
25117     },
25118     
25119     
25120     cleanUpChildren : function (n)
25121     {
25122         if (!n.childNodes.length) {
25123             return;
25124         }
25125         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25126            this.cleanUpChild(n.childNodes[i]);
25127         }
25128     },
25129     
25130     
25131         
25132     
25133     cleanUpChild : function (node)
25134     {
25135         var ed = this;
25136         //console.log(node);
25137         if (node.nodeName == "#text") {
25138             // clean up silly Windows -- stuff?
25139             return; 
25140         }
25141         if (node.nodeName == "#comment") {
25142             node.parentNode.removeChild(node);
25143             // clean up silly Windows -- stuff?
25144             return; 
25145         }
25146         var lcname = node.tagName.toLowerCase();
25147         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25148         // whitelist of tags..
25149         
25150         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25151             // remove node.
25152             node.parentNode.removeChild(node);
25153             return;
25154             
25155         }
25156         
25157         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25158         
25159         // spans with no attributes - just remove them..
25160         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25161             remove_keep_children = true;
25162         }
25163         
25164         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25165         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25166         
25167         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25168         //    remove_keep_children = true;
25169         //}
25170         
25171         if (remove_keep_children) {
25172             this.cleanUpChildren(node);
25173             // inserts everything just before this node...
25174             while (node.childNodes.length) {
25175                 var cn = node.childNodes[0];
25176                 node.removeChild(cn);
25177                 node.parentNode.insertBefore(cn, node);
25178             }
25179             node.parentNode.removeChild(node);
25180             return;
25181         }
25182         
25183         if (!node.attributes || !node.attributes.length) {
25184             
25185           
25186             
25187             
25188             this.cleanUpChildren(node);
25189             return;
25190         }
25191         
25192         function cleanAttr(n,v)
25193         {
25194             
25195             if (v.match(/^\./) || v.match(/^\//)) {
25196                 return;
25197             }
25198             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25199                 return;
25200             }
25201             if (v.match(/^#/)) {
25202                 return;
25203             }
25204             if (v.match(/^\{/)) { // allow template editing.
25205                 return;
25206             }
25207 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25208             node.removeAttribute(n);
25209             
25210         }
25211         
25212         var cwhite = this.cwhite;
25213         var cblack = this.cblack;
25214             
25215         function cleanStyle(n,v)
25216         {
25217             if (v.match(/expression/)) { //XSS?? should we even bother..
25218                 node.removeAttribute(n);
25219                 return;
25220             }
25221             
25222             var parts = v.split(/;/);
25223             var clean = [];
25224             
25225             Roo.each(parts, function(p) {
25226                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25227                 if (!p.length) {
25228                     return true;
25229                 }
25230                 var l = p.split(':').shift().replace(/\s+/g,'');
25231                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25232                 
25233                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25234 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25235                     //node.removeAttribute(n);
25236                     return true;
25237                 }
25238                 //Roo.log()
25239                 // only allow 'c whitelisted system attributes'
25240                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25241 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25242                     //node.removeAttribute(n);
25243                     return true;
25244                 }
25245                 
25246                 
25247                  
25248                 
25249                 clean.push(p);
25250                 return true;
25251             });
25252             if (clean.length) { 
25253                 node.setAttribute(n, clean.join(';'));
25254             } else {
25255                 node.removeAttribute(n);
25256             }
25257             
25258         }
25259         
25260         
25261         for (var i = node.attributes.length-1; i > -1 ; i--) {
25262             var a = node.attributes[i];
25263             //console.log(a);
25264             
25265             if (a.name.toLowerCase().substr(0,2)=='on')  {
25266                 node.removeAttribute(a.name);
25267                 continue;
25268             }
25269             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25270                 node.removeAttribute(a.name);
25271                 continue;
25272             }
25273             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25274                 cleanAttr(a.name,a.value); // fixme..
25275                 continue;
25276             }
25277             if (a.name == 'style') {
25278                 cleanStyle(a.name,a.value);
25279                 continue;
25280             }
25281             /// clean up MS crap..
25282             // tecnically this should be a list of valid class'es..
25283             
25284             
25285             if (a.name == 'class') {
25286                 if (a.value.match(/^Mso/)) {
25287                     node.removeAttribute('class');
25288                 }
25289                 
25290                 if (a.value.match(/^body$/)) {
25291                     node.removeAttribute('class');
25292                 }
25293                 continue;
25294             }
25295             
25296             // style cleanup!?
25297             // class cleanup?
25298             
25299         }
25300         
25301         
25302         this.cleanUpChildren(node);
25303         
25304         
25305     },
25306     
25307     /**
25308      * Clean up MS wordisms...
25309      */
25310     cleanWord : function(node)
25311     {
25312         if (!node) {
25313             this.cleanWord(this.doc.body);
25314             return;
25315         }
25316         
25317         if(
25318                 node.nodeName == 'SPAN' &&
25319                 !node.hasAttributes() &&
25320                 node.childNodes.length == 1 &&
25321                 node.firstChild.nodeName == "#text"  
25322         ) {
25323             var textNode = node.firstChild;
25324             node.removeChild(textNode);
25325             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25326                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25327             }
25328             node.parentNode.insertBefore(textNode, node);
25329             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25330                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25331             }
25332             node.parentNode.removeChild(node);
25333         }
25334         
25335         if (node.nodeName == "#text") {
25336             // clean up silly Windows -- stuff?
25337             return; 
25338         }
25339         if (node.nodeName == "#comment") {
25340             node.parentNode.removeChild(node);
25341             // clean up silly Windows -- stuff?
25342             return; 
25343         }
25344         
25345         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25346             node.parentNode.removeChild(node);
25347             return;
25348         }
25349         //Roo.log(node.tagName);
25350         // remove - but keep children..
25351         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25352             //Roo.log('-- removed');
25353             while (node.childNodes.length) {
25354                 var cn = node.childNodes[0];
25355                 node.removeChild(cn);
25356                 node.parentNode.insertBefore(cn, node);
25357                 // move node to parent - and clean it..
25358                 this.cleanWord(cn);
25359             }
25360             node.parentNode.removeChild(node);
25361             /// no need to iterate chidlren = it's got none..
25362             //this.iterateChildren(node, this.cleanWord);
25363             return;
25364         }
25365         // clean styles
25366         if (node.className.length) {
25367             
25368             var cn = node.className.split(/\W+/);
25369             var cna = [];
25370             Roo.each(cn, function(cls) {
25371                 if (cls.match(/Mso[a-zA-Z]+/)) {
25372                     return;
25373                 }
25374                 cna.push(cls);
25375             });
25376             node.className = cna.length ? cna.join(' ') : '';
25377             if (!cna.length) {
25378                 node.removeAttribute("class");
25379             }
25380         }
25381         
25382         if (node.hasAttribute("lang")) {
25383             node.removeAttribute("lang");
25384         }
25385         
25386         if (node.hasAttribute("style")) {
25387             
25388             var styles = node.getAttribute("style").split(";");
25389             var nstyle = [];
25390             Roo.each(styles, function(s) {
25391                 if (!s.match(/:/)) {
25392                     return;
25393                 }
25394                 var kv = s.split(":");
25395                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25396                     return;
25397                 }
25398                 // what ever is left... we allow.
25399                 nstyle.push(s);
25400             });
25401             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25402             if (!nstyle.length) {
25403                 node.removeAttribute('style');
25404             }
25405         }
25406         this.iterateChildren(node, this.cleanWord);
25407         
25408         
25409         
25410     },
25411     /**
25412      * iterateChildren of a Node, calling fn each time, using this as the scole..
25413      * @param {DomNode} node node to iterate children of.
25414      * @param {Function} fn method of this class to call on each item.
25415      */
25416     iterateChildren : function(node, fn)
25417     {
25418         if (!node.childNodes.length) {
25419                 return;
25420         }
25421         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25422            fn.call(this, node.childNodes[i])
25423         }
25424     },
25425     
25426     
25427     /**
25428      * cleanTableWidths.
25429      *
25430      * Quite often pasting from word etc.. results in tables with column and widths.
25431      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25432      *
25433      */
25434     cleanTableWidths : function(node)
25435     {
25436          
25437          
25438         if (!node) {
25439             this.cleanTableWidths(this.doc.body);
25440             return;
25441         }
25442         
25443         // ignore list...
25444         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25445             return; 
25446         }
25447         Roo.log(node.tagName);
25448         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25449             this.iterateChildren(node, this.cleanTableWidths);
25450             return;
25451         }
25452         if (node.hasAttribute('width')) {
25453             node.removeAttribute('width');
25454         }
25455         
25456          
25457         if (node.hasAttribute("style")) {
25458             // pretty basic...
25459             
25460             var styles = node.getAttribute("style").split(";");
25461             var nstyle = [];
25462             Roo.each(styles, function(s) {
25463                 if (!s.match(/:/)) {
25464                     return;
25465                 }
25466                 var kv = s.split(":");
25467                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25468                     return;
25469                 }
25470                 // what ever is left... we allow.
25471                 nstyle.push(s);
25472             });
25473             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25474             if (!nstyle.length) {
25475                 node.removeAttribute('style');
25476             }
25477         }
25478         
25479         this.iterateChildren(node, this.cleanTableWidths);
25480         
25481         
25482     },
25483     
25484     
25485     
25486     
25487     domToHTML : function(currentElement, depth, nopadtext) {
25488         
25489         depth = depth || 0;
25490         nopadtext = nopadtext || false;
25491     
25492         if (!currentElement) {
25493             return this.domToHTML(this.doc.body);
25494         }
25495         
25496         //Roo.log(currentElement);
25497         var j;
25498         var allText = false;
25499         var nodeName = currentElement.nodeName;
25500         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25501         
25502         if  (nodeName == '#text') {
25503             
25504             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25505         }
25506         
25507         
25508         var ret = '';
25509         if (nodeName != 'BODY') {
25510              
25511             var i = 0;
25512             // Prints the node tagName, such as <A>, <IMG>, etc
25513             if (tagName) {
25514                 var attr = [];
25515                 for(i = 0; i < currentElement.attributes.length;i++) {
25516                     // quoting?
25517                     var aname = currentElement.attributes.item(i).name;
25518                     if (!currentElement.attributes.item(i).value.length) {
25519                         continue;
25520                     }
25521                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25522                 }
25523                 
25524                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25525             } 
25526             else {
25527                 
25528                 // eack
25529             }
25530         } else {
25531             tagName = false;
25532         }
25533         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25534             return ret;
25535         }
25536         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25537             nopadtext = true;
25538         }
25539         
25540         
25541         // Traverse the tree
25542         i = 0;
25543         var currentElementChild = currentElement.childNodes.item(i);
25544         var allText = true;
25545         var innerHTML  = '';
25546         lastnode = '';
25547         while (currentElementChild) {
25548             // Formatting code (indent the tree so it looks nice on the screen)
25549             var nopad = nopadtext;
25550             if (lastnode == 'SPAN') {
25551                 nopad  = true;
25552             }
25553             // text
25554             if  (currentElementChild.nodeName == '#text') {
25555                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25556                 toadd = nopadtext ? toadd : toadd.trim();
25557                 if (!nopad && toadd.length > 80) {
25558                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25559                 }
25560                 innerHTML  += toadd;
25561                 
25562                 i++;
25563                 currentElementChild = currentElement.childNodes.item(i);
25564                 lastNode = '';
25565                 continue;
25566             }
25567             allText = false;
25568             
25569             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25570                 
25571             // Recursively traverse the tree structure of the child node
25572             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25573             lastnode = currentElementChild.nodeName;
25574             i++;
25575             currentElementChild=currentElement.childNodes.item(i);
25576         }
25577         
25578         ret += innerHTML;
25579         
25580         if (!allText) {
25581                 // The remaining code is mostly for formatting the tree
25582             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25583         }
25584         
25585         
25586         if (tagName) {
25587             ret+= "</"+tagName+">";
25588         }
25589         return ret;
25590         
25591     },
25592         
25593     applyBlacklists : function()
25594     {
25595         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25596         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25597         
25598         this.white = [];
25599         this.black = [];
25600         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25601             if (b.indexOf(tag) > -1) {
25602                 return;
25603             }
25604             this.white.push(tag);
25605             
25606         }, this);
25607         
25608         Roo.each(w, function(tag) {
25609             if (b.indexOf(tag) > -1) {
25610                 return;
25611             }
25612             if (this.white.indexOf(tag) > -1) {
25613                 return;
25614             }
25615             this.white.push(tag);
25616             
25617         }, this);
25618         
25619         
25620         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25621             if (w.indexOf(tag) > -1) {
25622                 return;
25623             }
25624             this.black.push(tag);
25625             
25626         }, this);
25627         
25628         Roo.each(b, function(tag) {
25629             if (w.indexOf(tag) > -1) {
25630                 return;
25631             }
25632             if (this.black.indexOf(tag) > -1) {
25633                 return;
25634             }
25635             this.black.push(tag);
25636             
25637         }, this);
25638         
25639         
25640         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25641         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25642         
25643         this.cwhite = [];
25644         this.cblack = [];
25645         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25646             if (b.indexOf(tag) > -1) {
25647                 return;
25648             }
25649             this.cwhite.push(tag);
25650             
25651         }, this);
25652         
25653         Roo.each(w, function(tag) {
25654             if (b.indexOf(tag) > -1) {
25655                 return;
25656             }
25657             if (this.cwhite.indexOf(tag) > -1) {
25658                 return;
25659             }
25660             this.cwhite.push(tag);
25661             
25662         }, this);
25663         
25664         
25665         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25666             if (w.indexOf(tag) > -1) {
25667                 return;
25668             }
25669             this.cblack.push(tag);
25670             
25671         }, this);
25672         
25673         Roo.each(b, function(tag) {
25674             if (w.indexOf(tag) > -1) {
25675                 return;
25676             }
25677             if (this.cblack.indexOf(tag) > -1) {
25678                 return;
25679             }
25680             this.cblack.push(tag);
25681             
25682         }, this);
25683     },
25684     
25685     setStylesheets : function(stylesheets)
25686     {
25687         if(typeof(stylesheets) == 'string'){
25688             Roo.get(this.iframe.contentDocument.head).createChild({
25689                 tag : 'link',
25690                 rel : 'stylesheet',
25691                 type : 'text/css',
25692                 href : stylesheets
25693             });
25694             
25695             return;
25696         }
25697         var _this = this;
25698      
25699         Roo.each(stylesheets, function(s) {
25700             if(!s.length){
25701                 return;
25702             }
25703             
25704             Roo.get(_this.iframe.contentDocument.head).createChild({
25705                 tag : 'link',
25706                 rel : 'stylesheet',
25707                 type : 'text/css',
25708                 href : s
25709             });
25710         });
25711
25712         
25713     },
25714     
25715     removeStylesheets : function()
25716     {
25717         var _this = this;
25718         
25719         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25720             s.remove();
25721         });
25722     },
25723     
25724     setStyle : function(style)
25725     {
25726         Roo.get(this.iframe.contentDocument.head).createChild({
25727             tag : 'style',
25728             type : 'text/css',
25729             html : style
25730         });
25731
25732         return;
25733     }
25734     
25735     // hide stuff that is not compatible
25736     /**
25737      * @event blur
25738      * @hide
25739      */
25740     /**
25741      * @event change
25742      * @hide
25743      */
25744     /**
25745      * @event focus
25746      * @hide
25747      */
25748     /**
25749      * @event specialkey
25750      * @hide
25751      */
25752     /**
25753      * @cfg {String} fieldClass @hide
25754      */
25755     /**
25756      * @cfg {String} focusClass @hide
25757      */
25758     /**
25759      * @cfg {String} autoCreate @hide
25760      */
25761     /**
25762      * @cfg {String} inputType @hide
25763      */
25764     /**
25765      * @cfg {String} invalidClass @hide
25766      */
25767     /**
25768      * @cfg {String} invalidText @hide
25769      */
25770     /**
25771      * @cfg {String} msgFx @hide
25772      */
25773     /**
25774      * @cfg {String} validateOnBlur @hide
25775      */
25776 });
25777
25778 Roo.HtmlEditorCore.white = [
25779         'area', 'br', 'img', 'input', 'hr', 'wbr',
25780         
25781        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25782        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25783        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25784        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25785        'table',   'ul',         'xmp', 
25786        
25787        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25788       'thead',   'tr', 
25789      
25790       'dir', 'menu', 'ol', 'ul', 'dl',
25791        
25792       'embed',  'object'
25793 ];
25794
25795
25796 Roo.HtmlEditorCore.black = [
25797     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25798         'applet', // 
25799         'base',   'basefont', 'bgsound', 'blink',  'body', 
25800         'frame',  'frameset', 'head',    'html',   'ilayer', 
25801         'iframe', 'layer',  'link',     'meta',    'object',   
25802         'script', 'style' ,'title',  'xml' // clean later..
25803 ];
25804 Roo.HtmlEditorCore.clean = [
25805     'script', 'style', 'title', 'xml'
25806 ];
25807 Roo.HtmlEditorCore.remove = [
25808     'font'
25809 ];
25810 // attributes..
25811
25812 Roo.HtmlEditorCore.ablack = [
25813     'on'
25814 ];
25815     
25816 Roo.HtmlEditorCore.aclean = [ 
25817     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25818 ];
25819
25820 // protocols..
25821 Roo.HtmlEditorCore.pwhite= [
25822         'http',  'https',  'mailto'
25823 ];
25824
25825 // white listed style attributes.
25826 Roo.HtmlEditorCore.cwhite= [
25827       //  'text-align', /// default is to allow most things..
25828       
25829          
25830 //        'font-size'//??
25831 ];
25832
25833 // black listed style attributes.
25834 Roo.HtmlEditorCore.cblack= [
25835       //  'font-size' -- this can be set by the project 
25836 ];
25837
25838
25839 Roo.HtmlEditorCore.swapCodes   =[ 
25840     [    8211, "&#8211;" ], 
25841     [    8212, "&#8212;" ], 
25842     [    8216,  "'" ],  
25843     [    8217, "'" ],  
25844     [    8220, '"' ],  
25845     [    8221, '"' ],  
25846     [    8226, "*" ],  
25847     [    8230, "..." ]
25848 ]; 
25849
25850     /*
25851  * - LGPL
25852  *
25853  * HtmlEditor
25854  * 
25855  */
25856
25857 /**
25858  * @class Roo.bootstrap.HtmlEditor
25859  * @extends Roo.bootstrap.TextArea
25860  * Bootstrap HtmlEditor class
25861
25862  * @constructor
25863  * Create a new HtmlEditor
25864  * @param {Object} config The config object
25865  */
25866
25867 Roo.bootstrap.HtmlEditor = function(config){
25868     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25869     if (!this.toolbars) {
25870         this.toolbars = [];
25871     }
25872     
25873     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25874     this.addEvents({
25875             /**
25876              * @event initialize
25877              * Fires when the editor is fully initialized (including the iframe)
25878              * @param {HtmlEditor} this
25879              */
25880             initialize: true,
25881             /**
25882              * @event activate
25883              * Fires when the editor is first receives the focus. Any insertion must wait
25884              * until after this event.
25885              * @param {HtmlEditor} this
25886              */
25887             activate: true,
25888              /**
25889              * @event beforesync
25890              * Fires before the textarea is updated with content from the editor iframe. Return false
25891              * to cancel the sync.
25892              * @param {HtmlEditor} this
25893              * @param {String} html
25894              */
25895             beforesync: true,
25896              /**
25897              * @event beforepush
25898              * Fires before the iframe editor is updated with content from the textarea. Return false
25899              * to cancel the push.
25900              * @param {HtmlEditor} this
25901              * @param {String} html
25902              */
25903             beforepush: true,
25904              /**
25905              * @event sync
25906              * Fires when the textarea is updated with content from the editor iframe.
25907              * @param {HtmlEditor} this
25908              * @param {String} html
25909              */
25910             sync: true,
25911              /**
25912              * @event push
25913              * Fires when the iframe editor is updated with content from the textarea.
25914              * @param {HtmlEditor} this
25915              * @param {String} html
25916              */
25917             push: true,
25918              /**
25919              * @event editmodechange
25920              * Fires when the editor switches edit modes
25921              * @param {HtmlEditor} this
25922              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25923              */
25924             editmodechange: true,
25925             /**
25926              * @event editorevent
25927              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25928              * @param {HtmlEditor} this
25929              */
25930             editorevent: true,
25931             /**
25932              * @event firstfocus
25933              * Fires when on first focus - needed by toolbars..
25934              * @param {HtmlEditor} this
25935              */
25936             firstfocus: true,
25937             /**
25938              * @event autosave
25939              * Auto save the htmlEditor value as a file into Events
25940              * @param {HtmlEditor} this
25941              */
25942             autosave: true,
25943             /**
25944              * @event savedpreview
25945              * preview the saved version of htmlEditor
25946              * @param {HtmlEditor} this
25947              */
25948             savedpreview: true
25949         });
25950 };
25951
25952
25953 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25954     
25955     
25956       /**
25957      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25958      */
25959     toolbars : false,
25960     
25961      /**
25962     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25963     */
25964     btns : [],
25965    
25966      /**
25967      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25968      *                        Roo.resizable.
25969      */
25970     resizable : false,
25971      /**
25972      * @cfg {Number} height (in pixels)
25973      */   
25974     height: 300,
25975    /**
25976      * @cfg {Number} width (in pixels)
25977      */   
25978     width: false,
25979     
25980     /**
25981      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25982      * 
25983      */
25984     stylesheets: false,
25985     
25986     // id of frame..
25987     frameId: false,
25988     
25989     // private properties
25990     validationEvent : false,
25991     deferHeight: true,
25992     initialized : false,
25993     activated : false,
25994     
25995     onFocus : Roo.emptyFn,
25996     iframePad:3,
25997     hideMode:'offsets',
25998     
25999     tbContainer : false,
26000     
26001     bodyCls : '',
26002     
26003     toolbarContainer :function() {
26004         return this.wrap.select('.x-html-editor-tb',true).first();
26005     },
26006
26007     /**
26008      * Protected method that will not generally be called directly. It
26009      * is called when the editor creates its toolbar. Override this method if you need to
26010      * add custom toolbar buttons.
26011      * @param {HtmlEditor} editor
26012      */
26013     createToolbar : function(){
26014         Roo.log('renewing');
26015         Roo.log("create toolbars");
26016         
26017         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26018         this.toolbars[0].render(this.toolbarContainer());
26019         
26020         return;
26021         
26022 //        if (!editor.toolbars || !editor.toolbars.length) {
26023 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26024 //        }
26025 //        
26026 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26027 //            editor.toolbars[i] = Roo.factory(
26028 //                    typeof(editor.toolbars[i]) == 'string' ?
26029 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26030 //                Roo.bootstrap.HtmlEditor);
26031 //            editor.toolbars[i].init(editor);
26032 //        }
26033     },
26034
26035      
26036     // private
26037     onRender : function(ct, position)
26038     {
26039        // Roo.log("Call onRender: " + this.xtype);
26040         var _t = this;
26041         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26042       
26043         this.wrap = this.inputEl().wrap({
26044             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26045         });
26046         
26047         this.editorcore.onRender(ct, position);
26048          
26049         if (this.resizable) {
26050             this.resizeEl = new Roo.Resizable(this.wrap, {
26051                 pinned : true,
26052                 wrap: true,
26053                 dynamic : true,
26054                 minHeight : this.height,
26055                 height: this.height,
26056                 handles : this.resizable,
26057                 width: this.width,
26058                 listeners : {
26059                     resize : function(r, w, h) {
26060                         _t.onResize(w,h); // -something
26061                     }
26062                 }
26063             });
26064             
26065         }
26066         this.createToolbar(this);
26067        
26068         
26069         if(!this.width && this.resizable){
26070             this.setSize(this.wrap.getSize());
26071         }
26072         if (this.resizeEl) {
26073             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26074             // should trigger onReize..
26075         }
26076         
26077     },
26078
26079     // private
26080     onResize : function(w, h)
26081     {
26082         Roo.log('resize: ' +w + ',' + h );
26083         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26084         var ew = false;
26085         var eh = false;
26086         
26087         if(this.inputEl() ){
26088             if(typeof w == 'number'){
26089                 var aw = w - this.wrap.getFrameWidth('lr');
26090                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26091                 ew = aw;
26092             }
26093             if(typeof h == 'number'){
26094                  var tbh = -11;  // fixme it needs to tool bar size!
26095                 for (var i =0; i < this.toolbars.length;i++) {
26096                     // fixme - ask toolbars for heights?
26097                     tbh += this.toolbars[i].el.getHeight();
26098                     //if (this.toolbars[i].footer) {
26099                     //    tbh += this.toolbars[i].footer.el.getHeight();
26100                     //}
26101                 }
26102               
26103                 
26104                 
26105                 
26106                 
26107                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26108                 ah -= 5; // knock a few pixes off for look..
26109                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26110                 var eh = ah;
26111             }
26112         }
26113         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26114         this.editorcore.onResize(ew,eh);
26115         
26116     },
26117
26118     /**
26119      * Toggles the editor between standard and source edit mode.
26120      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26121      */
26122     toggleSourceEdit : function(sourceEditMode)
26123     {
26124         this.editorcore.toggleSourceEdit(sourceEditMode);
26125         
26126         if(this.editorcore.sourceEditMode){
26127             Roo.log('editor - showing textarea');
26128             
26129 //            Roo.log('in');
26130 //            Roo.log(this.syncValue());
26131             this.syncValue();
26132             this.inputEl().removeClass(['hide', 'x-hidden']);
26133             this.inputEl().dom.removeAttribute('tabIndex');
26134             this.inputEl().focus();
26135         }else{
26136             Roo.log('editor - hiding textarea');
26137 //            Roo.log('out')
26138 //            Roo.log(this.pushValue()); 
26139             this.pushValue();
26140             
26141             this.inputEl().addClass(['hide', 'x-hidden']);
26142             this.inputEl().dom.setAttribute('tabIndex', -1);
26143             //this.deferFocus();
26144         }
26145          
26146         if(this.resizable){
26147             this.setSize(this.wrap.getSize());
26148         }
26149         
26150         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26151     },
26152  
26153     // private (for BoxComponent)
26154     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26155
26156     // private (for BoxComponent)
26157     getResizeEl : function(){
26158         return this.wrap;
26159     },
26160
26161     // private (for BoxComponent)
26162     getPositionEl : function(){
26163         return this.wrap;
26164     },
26165
26166     // private
26167     initEvents : function(){
26168         this.originalValue = this.getValue();
26169     },
26170
26171 //    /**
26172 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26173 //     * @method
26174 //     */
26175 //    markInvalid : Roo.emptyFn,
26176 //    /**
26177 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26178 //     * @method
26179 //     */
26180 //    clearInvalid : Roo.emptyFn,
26181
26182     setValue : function(v){
26183         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26184         this.editorcore.pushValue();
26185     },
26186
26187      
26188     // private
26189     deferFocus : function(){
26190         this.focus.defer(10, this);
26191     },
26192
26193     // doc'ed in Field
26194     focus : function(){
26195         this.editorcore.focus();
26196         
26197     },
26198       
26199
26200     // private
26201     onDestroy : function(){
26202         
26203         
26204         
26205         if(this.rendered){
26206             
26207             for (var i =0; i < this.toolbars.length;i++) {
26208                 // fixme - ask toolbars for heights?
26209                 this.toolbars[i].onDestroy();
26210             }
26211             
26212             this.wrap.dom.innerHTML = '';
26213             this.wrap.remove();
26214         }
26215     },
26216
26217     // private
26218     onFirstFocus : function(){
26219         //Roo.log("onFirstFocus");
26220         this.editorcore.onFirstFocus();
26221          for (var i =0; i < this.toolbars.length;i++) {
26222             this.toolbars[i].onFirstFocus();
26223         }
26224         
26225     },
26226     
26227     // private
26228     syncValue : function()
26229     {   
26230         this.editorcore.syncValue();
26231     },
26232     
26233     pushValue : function()
26234     {   
26235         this.editorcore.pushValue();
26236     }
26237      
26238     
26239     // hide stuff that is not compatible
26240     /**
26241      * @event blur
26242      * @hide
26243      */
26244     /**
26245      * @event change
26246      * @hide
26247      */
26248     /**
26249      * @event focus
26250      * @hide
26251      */
26252     /**
26253      * @event specialkey
26254      * @hide
26255      */
26256     /**
26257      * @cfg {String} fieldClass @hide
26258      */
26259     /**
26260      * @cfg {String} focusClass @hide
26261      */
26262     /**
26263      * @cfg {String} autoCreate @hide
26264      */
26265     /**
26266      * @cfg {String} inputType @hide
26267      */
26268      
26269     /**
26270      * @cfg {String} invalidText @hide
26271      */
26272     /**
26273      * @cfg {String} msgFx @hide
26274      */
26275     /**
26276      * @cfg {String} validateOnBlur @hide
26277      */
26278 });
26279  
26280     
26281    
26282    
26283    
26284       
26285 Roo.namespace('Roo.bootstrap.htmleditor');
26286 /**
26287  * @class Roo.bootstrap.HtmlEditorToolbar1
26288  * Basic Toolbar
26289  * 
26290  * @example
26291  * Usage:
26292  *
26293  new Roo.bootstrap.HtmlEditor({
26294     ....
26295     toolbars : [
26296         new Roo.bootstrap.HtmlEditorToolbar1({
26297             disable : { fonts: 1 , format: 1, ..., ... , ...],
26298             btns : [ .... ]
26299         })
26300     }
26301      
26302  * 
26303  * @cfg {Object} disable List of elements to disable..
26304  * @cfg {Array} btns List of additional buttons.
26305  * 
26306  * 
26307  * NEEDS Extra CSS? 
26308  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26309  */
26310  
26311 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26312 {
26313     
26314     Roo.apply(this, config);
26315     
26316     // default disabled, based on 'good practice'..
26317     this.disable = this.disable || {};
26318     Roo.applyIf(this.disable, {
26319         fontSize : true,
26320         colors : true,
26321         specialElements : true
26322     });
26323     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26324     
26325     this.editor = config.editor;
26326     this.editorcore = config.editor.editorcore;
26327     
26328     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26329     
26330     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26331     // dont call parent... till later.
26332 }
26333 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26334      
26335     bar : true,
26336     
26337     editor : false,
26338     editorcore : false,
26339     
26340     
26341     formats : [
26342         "p" ,  
26343         "h1","h2","h3","h4","h5","h6", 
26344         "pre", "code", 
26345         "abbr", "acronym", "address", "cite", "samp", "var",
26346         'div','span'
26347     ],
26348     
26349     onRender : function(ct, position)
26350     {
26351        // Roo.log("Call onRender: " + this.xtype);
26352         
26353        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26354        Roo.log(this.el);
26355        this.el.dom.style.marginBottom = '0';
26356        var _this = this;
26357        var editorcore = this.editorcore;
26358        var editor= this.editor;
26359        
26360        var children = [];
26361        var btn = function(id,cmd , toggle, handler, html){
26362        
26363             var  event = toggle ? 'toggle' : 'click';
26364        
26365             var a = {
26366                 size : 'sm',
26367                 xtype: 'Button',
26368                 xns: Roo.bootstrap,
26369                 //glyphicon : id,
26370                 fa: id,
26371                 cmd : id || cmd,
26372                 enableToggle:toggle !== false,
26373                 html : html || '',
26374                 pressed : toggle ? false : null,
26375                 listeners : {}
26376             };
26377             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26378                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26379             };
26380             children.push(a);
26381             return a;
26382        }
26383        
26384     //    var cb_box = function...
26385         
26386         var style = {
26387                 xtype: 'Button',
26388                 size : 'sm',
26389                 xns: Roo.bootstrap,
26390                 fa : 'font',
26391                 //html : 'submit'
26392                 menu : {
26393                     xtype: 'Menu',
26394                     xns: Roo.bootstrap,
26395                     items:  []
26396                 }
26397         };
26398         Roo.each(this.formats, function(f) {
26399             style.menu.items.push({
26400                 xtype :'MenuItem',
26401                 xns: Roo.bootstrap,
26402                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26403                 tagname : f,
26404                 listeners : {
26405                     click : function()
26406                     {
26407                         editorcore.insertTag(this.tagname);
26408                         editor.focus();
26409                     }
26410                 }
26411                 
26412             });
26413         });
26414         children.push(style);   
26415         
26416         btn('bold',false,true);
26417         btn('italic',false,true);
26418         btn('align-left', 'justifyleft',true);
26419         btn('align-center', 'justifycenter',true);
26420         btn('align-right' , 'justifyright',true);
26421         btn('link', false, false, function(btn) {
26422             //Roo.log("create link?");
26423             var url = prompt(this.createLinkText, this.defaultLinkValue);
26424             if(url && url != 'http:/'+'/'){
26425                 this.editorcore.relayCmd('createlink', url);
26426             }
26427         }),
26428         btn('list','insertunorderedlist',true);
26429         btn('pencil', false,true, function(btn){
26430                 Roo.log(this);
26431                 this.toggleSourceEdit(btn.pressed);
26432         });
26433         
26434         if (this.editor.btns.length > 0) {
26435             for (var i = 0; i<this.editor.btns.length; i++) {
26436                 children.push(this.editor.btns[i]);
26437             }
26438         }
26439         
26440         /*
26441         var cog = {
26442                 xtype: 'Button',
26443                 size : 'sm',
26444                 xns: Roo.bootstrap,
26445                 glyphicon : 'cog',
26446                 //html : 'submit'
26447                 menu : {
26448                     xtype: 'Menu',
26449                     xns: Roo.bootstrap,
26450                     items:  []
26451                 }
26452         };
26453         
26454         cog.menu.items.push({
26455             xtype :'MenuItem',
26456             xns: Roo.bootstrap,
26457             html : Clean styles,
26458             tagname : f,
26459             listeners : {
26460                 click : function()
26461                 {
26462                     editorcore.insertTag(this.tagname);
26463                     editor.focus();
26464                 }
26465             }
26466             
26467         });
26468        */
26469         
26470          
26471        this.xtype = 'NavSimplebar';
26472         
26473         for(var i=0;i< children.length;i++) {
26474             
26475             this.buttons.add(this.addxtypeChild(children[i]));
26476             
26477         }
26478         
26479         editor.on('editorevent', this.updateToolbar, this);
26480     },
26481     onBtnClick : function(id)
26482     {
26483        this.editorcore.relayCmd(id);
26484        this.editorcore.focus();
26485     },
26486     
26487     /**
26488      * Protected method that will not generally be called directly. It triggers
26489      * a toolbar update by reading the markup state of the current selection in the editor.
26490      */
26491     updateToolbar: function(){
26492
26493         if(!this.editorcore.activated){
26494             this.editor.onFirstFocus(); // is this neeed?
26495             return;
26496         }
26497
26498         var btns = this.buttons; 
26499         var doc = this.editorcore.doc;
26500         btns.get('bold').setActive(doc.queryCommandState('bold'));
26501         btns.get('italic').setActive(doc.queryCommandState('italic'));
26502         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26503         
26504         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26505         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26506         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26507         
26508         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26509         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26510          /*
26511         
26512         var ans = this.editorcore.getAllAncestors();
26513         if (this.formatCombo) {
26514             
26515             
26516             var store = this.formatCombo.store;
26517             this.formatCombo.setValue("");
26518             for (var i =0; i < ans.length;i++) {
26519                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26520                     // select it..
26521                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26522                     break;
26523                 }
26524             }
26525         }
26526         
26527         
26528         
26529         // hides menus... - so this cant be on a menu...
26530         Roo.bootstrap.MenuMgr.hideAll();
26531         */
26532         Roo.bootstrap.MenuMgr.hideAll();
26533         //this.editorsyncValue();
26534     },
26535     onFirstFocus: function() {
26536         this.buttons.each(function(item){
26537            item.enable();
26538         });
26539     },
26540     toggleSourceEdit : function(sourceEditMode){
26541         
26542           
26543         if(sourceEditMode){
26544             Roo.log("disabling buttons");
26545            this.buttons.each( function(item){
26546                 if(item.cmd != 'pencil'){
26547                     item.disable();
26548                 }
26549             });
26550           
26551         }else{
26552             Roo.log("enabling buttons");
26553             if(this.editorcore.initialized){
26554                 this.buttons.each( function(item){
26555                     item.enable();
26556                 });
26557             }
26558             
26559         }
26560         Roo.log("calling toggole on editor");
26561         // tell the editor that it's been pressed..
26562         this.editor.toggleSourceEdit(sourceEditMode);
26563        
26564     }
26565 });
26566
26567
26568
26569
26570  
26571 /*
26572  * - LGPL
26573  */
26574
26575 /**
26576  * @class Roo.bootstrap.Markdown
26577  * @extends Roo.bootstrap.TextArea
26578  * Bootstrap Showdown editable area
26579  * @cfg {string} content
26580  * 
26581  * @constructor
26582  * Create a new Showdown
26583  */
26584
26585 Roo.bootstrap.Markdown = function(config){
26586     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26587    
26588 };
26589
26590 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26591     
26592     editing :false,
26593     
26594     initEvents : function()
26595     {
26596         
26597         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26598         this.markdownEl = this.el.createChild({
26599             cls : 'roo-markdown-area'
26600         });
26601         this.inputEl().addClass('d-none');
26602         if (this.getValue() == '') {
26603             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26604             
26605         } else {
26606             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26607         }
26608         this.markdownEl.on('click', this.toggleTextEdit, this);
26609         this.on('blur', this.toggleTextEdit, this);
26610         this.on('specialkey', this.resizeTextArea, this);
26611     },
26612     
26613     toggleTextEdit : function()
26614     {
26615         var sh = this.markdownEl.getHeight();
26616         this.inputEl().addClass('d-none');
26617         this.markdownEl.addClass('d-none');
26618         if (!this.editing) {
26619             // show editor?
26620             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26621             this.inputEl().removeClass('d-none');
26622             this.inputEl().focus();
26623             this.editing = true;
26624             return;
26625         }
26626         // show showdown...
26627         this.updateMarkdown();
26628         this.markdownEl.removeClass('d-none');
26629         this.editing = false;
26630         return;
26631     },
26632     updateMarkdown : function()
26633     {
26634         if (this.getValue() == '') {
26635             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26636             return;
26637         }
26638  
26639         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26640     },
26641     
26642     resizeTextArea: function () {
26643         
26644         var sh = 100;
26645         Roo.log([sh, this.getValue().split("\n").length * 30]);
26646         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26647     },
26648     setValue : function(val)
26649     {
26650         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26651         if (!this.editing) {
26652             this.updateMarkdown();
26653         }
26654         
26655     },
26656     focus : function()
26657     {
26658         if (!this.editing) {
26659             this.toggleTextEdit();
26660         }
26661         
26662     }
26663
26664
26665 });
26666 /**
26667  * @class Roo.bootstrap.Table.AbstractSelectionModel
26668  * @extends Roo.util.Observable
26669  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26670  * implemented by descendant classes.  This class should not be directly instantiated.
26671  * @constructor
26672  */
26673 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26674     this.locked = false;
26675     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26676 };
26677
26678
26679 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26680     /** @ignore Called by the grid automatically. Do not call directly. */
26681     init : function(grid){
26682         this.grid = grid;
26683         this.initEvents();
26684     },
26685
26686     /**
26687      * Locks the selections.
26688      */
26689     lock : function(){
26690         this.locked = true;
26691     },
26692
26693     /**
26694      * Unlocks the selections.
26695      */
26696     unlock : function(){
26697         this.locked = false;
26698     },
26699
26700     /**
26701      * Returns true if the selections are locked.
26702      * @return {Boolean}
26703      */
26704     isLocked : function(){
26705         return this.locked;
26706     },
26707     
26708     
26709     initEvents : function ()
26710     {
26711         
26712     }
26713 });
26714 /**
26715  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26716  * @class Roo.bootstrap.Table.RowSelectionModel
26717  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26718  * It supports multiple selections and keyboard selection/navigation. 
26719  * @constructor
26720  * @param {Object} config
26721  */
26722
26723 Roo.bootstrap.Table.RowSelectionModel = function(config){
26724     Roo.apply(this, config);
26725     this.selections = new Roo.util.MixedCollection(false, function(o){
26726         return o.id;
26727     });
26728
26729     this.last = false;
26730     this.lastActive = false;
26731
26732     this.addEvents({
26733         /**
26734              * @event selectionchange
26735              * Fires when the selection changes
26736              * @param {SelectionModel} this
26737              */
26738             "selectionchange" : true,
26739         /**
26740              * @event afterselectionchange
26741              * Fires after the selection changes (eg. by key press or clicking)
26742              * @param {SelectionModel} this
26743              */
26744             "afterselectionchange" : true,
26745         /**
26746              * @event beforerowselect
26747              * Fires when a row is selected being selected, return false to cancel.
26748              * @param {SelectionModel} this
26749              * @param {Number} rowIndex The selected index
26750              * @param {Boolean} keepExisting False if other selections will be cleared
26751              */
26752             "beforerowselect" : true,
26753         /**
26754              * @event rowselect
26755              * Fires when a row is selected.
26756              * @param {SelectionModel} this
26757              * @param {Number} rowIndex The selected index
26758              * @param {Roo.data.Record} r The record
26759              */
26760             "rowselect" : true,
26761         /**
26762              * @event rowdeselect
26763              * Fires when a row is deselected.
26764              * @param {SelectionModel} this
26765              * @param {Number} rowIndex The selected index
26766              */
26767         "rowdeselect" : true
26768     });
26769     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26770     this.locked = false;
26771  };
26772
26773 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26774     /**
26775      * @cfg {Boolean} singleSelect
26776      * True to allow selection of only one row at a time (defaults to false)
26777      */
26778     singleSelect : false,
26779
26780     // private
26781     initEvents : function()
26782     {
26783
26784         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26785         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26786         //}else{ // allow click to work like normal
26787          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26788         //}
26789         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26790         this.grid.on("rowclick", this.handleMouseDown, this);
26791         
26792         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26793             "up" : function(e){
26794                 if(!e.shiftKey){
26795                     this.selectPrevious(e.shiftKey);
26796                 }else if(this.last !== false && this.lastActive !== false){
26797                     var last = this.last;
26798                     this.selectRange(this.last,  this.lastActive-1);
26799                     this.grid.getView().focusRow(this.lastActive);
26800                     if(last !== false){
26801                         this.last = last;
26802                     }
26803                 }else{
26804                     this.selectFirstRow();
26805                 }
26806                 this.fireEvent("afterselectionchange", this);
26807             },
26808             "down" : function(e){
26809                 if(!e.shiftKey){
26810                     this.selectNext(e.shiftKey);
26811                 }else if(this.last !== false && this.lastActive !== false){
26812                     var last = this.last;
26813                     this.selectRange(this.last,  this.lastActive+1);
26814                     this.grid.getView().focusRow(this.lastActive);
26815                     if(last !== false){
26816                         this.last = last;
26817                     }
26818                 }else{
26819                     this.selectFirstRow();
26820                 }
26821                 this.fireEvent("afterselectionchange", this);
26822             },
26823             scope: this
26824         });
26825         this.grid.store.on('load', function(){
26826             this.selections.clear();
26827         },this);
26828         /*
26829         var view = this.grid.view;
26830         view.on("refresh", this.onRefresh, this);
26831         view.on("rowupdated", this.onRowUpdated, this);
26832         view.on("rowremoved", this.onRemove, this);
26833         */
26834     },
26835
26836     // private
26837     onRefresh : function()
26838     {
26839         var ds = this.grid.store, i, v = this.grid.view;
26840         var s = this.selections;
26841         s.each(function(r){
26842             if((i = ds.indexOfId(r.id)) != -1){
26843                 v.onRowSelect(i);
26844             }else{
26845                 s.remove(r);
26846             }
26847         });
26848     },
26849
26850     // private
26851     onRemove : function(v, index, r){
26852         this.selections.remove(r);
26853     },
26854
26855     // private
26856     onRowUpdated : function(v, index, r){
26857         if(this.isSelected(r)){
26858             v.onRowSelect(index);
26859         }
26860     },
26861
26862     /**
26863      * Select records.
26864      * @param {Array} records The records to select
26865      * @param {Boolean} keepExisting (optional) True to keep existing selections
26866      */
26867     selectRecords : function(records, keepExisting)
26868     {
26869         if(!keepExisting){
26870             this.clearSelections();
26871         }
26872             var ds = this.grid.store;
26873         for(var i = 0, len = records.length; i < len; i++){
26874             this.selectRow(ds.indexOf(records[i]), true);
26875         }
26876     },
26877
26878     /**
26879      * Gets the number of selected rows.
26880      * @return {Number}
26881      */
26882     getCount : function(){
26883         return this.selections.length;
26884     },
26885
26886     /**
26887      * Selects the first row in the grid.
26888      */
26889     selectFirstRow : function(){
26890         this.selectRow(0);
26891     },
26892
26893     /**
26894      * Select the last row.
26895      * @param {Boolean} keepExisting (optional) True to keep existing selections
26896      */
26897     selectLastRow : function(keepExisting){
26898         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26899         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26900     },
26901
26902     /**
26903      * Selects the row immediately following the last selected row.
26904      * @param {Boolean} keepExisting (optional) True to keep existing selections
26905      */
26906     selectNext : function(keepExisting)
26907     {
26908             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26909             this.selectRow(this.last+1, keepExisting);
26910             this.grid.getView().focusRow(this.last);
26911         }
26912     },
26913
26914     /**
26915      * Selects the row that precedes the last selected row.
26916      * @param {Boolean} keepExisting (optional) True to keep existing selections
26917      */
26918     selectPrevious : function(keepExisting){
26919         if(this.last){
26920             this.selectRow(this.last-1, keepExisting);
26921             this.grid.getView().focusRow(this.last);
26922         }
26923     },
26924
26925     /**
26926      * Returns the selected records
26927      * @return {Array} Array of selected records
26928      */
26929     getSelections : function(){
26930         return [].concat(this.selections.items);
26931     },
26932
26933     /**
26934      * Returns the first selected record.
26935      * @return {Record}
26936      */
26937     getSelected : function(){
26938         return this.selections.itemAt(0);
26939     },
26940
26941
26942     /**
26943      * Clears all selections.
26944      */
26945     clearSelections : function(fast)
26946     {
26947         if(this.locked) {
26948             return;
26949         }
26950         if(fast !== true){
26951                 var ds = this.grid.store;
26952             var s = this.selections;
26953             s.each(function(r){
26954                 this.deselectRow(ds.indexOfId(r.id));
26955             }, this);
26956             s.clear();
26957         }else{
26958             this.selections.clear();
26959         }
26960         this.last = false;
26961     },
26962
26963
26964     /**
26965      * Selects all rows.
26966      */
26967     selectAll : function(){
26968         if(this.locked) {
26969             return;
26970         }
26971         this.selections.clear();
26972         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26973             this.selectRow(i, true);
26974         }
26975     },
26976
26977     /**
26978      * Returns True if there is a selection.
26979      * @return {Boolean}
26980      */
26981     hasSelection : function(){
26982         return this.selections.length > 0;
26983     },
26984
26985     /**
26986      * Returns True if the specified row is selected.
26987      * @param {Number/Record} record The record or index of the record to check
26988      * @return {Boolean}
26989      */
26990     isSelected : function(index){
26991             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26992         return (r && this.selections.key(r.id) ? true : false);
26993     },
26994
26995     /**
26996      * Returns True if the specified record id is selected.
26997      * @param {String} id The id of record to check
26998      * @return {Boolean}
26999      */
27000     isIdSelected : function(id){
27001         return (this.selections.key(id) ? true : false);
27002     },
27003
27004
27005     // private
27006     handleMouseDBClick : function(e, t){
27007         
27008     },
27009     // private
27010     handleMouseDown : function(e, t)
27011     {
27012             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27013         if(this.isLocked() || rowIndex < 0 ){
27014             return;
27015         };
27016         if(e.shiftKey && this.last !== false){
27017             var last = this.last;
27018             this.selectRange(last, rowIndex, e.ctrlKey);
27019             this.last = last; // reset the last
27020             t.focus();
27021     
27022         }else{
27023             var isSelected = this.isSelected(rowIndex);
27024             //Roo.log("select row:" + rowIndex);
27025             if(isSelected){
27026                 this.deselectRow(rowIndex);
27027             } else {
27028                         this.selectRow(rowIndex, true);
27029             }
27030     
27031             /*
27032                 if(e.button !== 0 && isSelected){
27033                 alert('rowIndex 2: ' + rowIndex);
27034                     view.focusRow(rowIndex);
27035                 }else if(e.ctrlKey && isSelected){
27036                     this.deselectRow(rowIndex);
27037                 }else if(!isSelected){
27038                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27039                     view.focusRow(rowIndex);
27040                 }
27041             */
27042         }
27043         this.fireEvent("afterselectionchange", this);
27044     },
27045     // private
27046     handleDragableRowClick :  function(grid, rowIndex, e) 
27047     {
27048         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27049             this.selectRow(rowIndex, false);
27050             grid.view.focusRow(rowIndex);
27051              this.fireEvent("afterselectionchange", this);
27052         }
27053     },
27054     
27055     /**
27056      * Selects multiple rows.
27057      * @param {Array} rows Array of the indexes of the row to select
27058      * @param {Boolean} keepExisting (optional) True to keep existing selections
27059      */
27060     selectRows : function(rows, keepExisting){
27061         if(!keepExisting){
27062             this.clearSelections();
27063         }
27064         for(var i = 0, len = rows.length; i < len; i++){
27065             this.selectRow(rows[i], true);
27066         }
27067     },
27068
27069     /**
27070      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27071      * @param {Number} startRow The index of the first row in the range
27072      * @param {Number} endRow The index of the last row in the range
27073      * @param {Boolean} keepExisting (optional) True to retain existing selections
27074      */
27075     selectRange : function(startRow, endRow, keepExisting){
27076         if(this.locked) {
27077             return;
27078         }
27079         if(!keepExisting){
27080             this.clearSelections();
27081         }
27082         if(startRow <= endRow){
27083             for(var i = startRow; i <= endRow; i++){
27084                 this.selectRow(i, true);
27085             }
27086         }else{
27087             for(var i = startRow; i >= endRow; i--){
27088                 this.selectRow(i, true);
27089             }
27090         }
27091     },
27092
27093     /**
27094      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27095      * @param {Number} startRow The index of the first row in the range
27096      * @param {Number} endRow The index of the last row in the range
27097      */
27098     deselectRange : function(startRow, endRow, preventViewNotify){
27099         if(this.locked) {
27100             return;
27101         }
27102         for(var i = startRow; i <= endRow; i++){
27103             this.deselectRow(i, preventViewNotify);
27104         }
27105     },
27106
27107     /**
27108      * Selects a row.
27109      * @param {Number} row The index of the row to select
27110      * @param {Boolean} keepExisting (optional) True to keep existing selections
27111      */
27112     selectRow : function(index, keepExisting, preventViewNotify)
27113     {
27114             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27115             return;
27116         }
27117         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27118             if(!keepExisting || this.singleSelect){
27119                 this.clearSelections();
27120             }
27121             
27122             var r = this.grid.store.getAt(index);
27123             //console.log('selectRow - record id :' + r.id);
27124             
27125             this.selections.add(r);
27126             this.last = this.lastActive = index;
27127             if(!preventViewNotify){
27128                 var proxy = new Roo.Element(
27129                                 this.grid.getRowDom(index)
27130                 );
27131                 proxy.addClass('bg-info info');
27132             }
27133             this.fireEvent("rowselect", this, index, r);
27134             this.fireEvent("selectionchange", this);
27135         }
27136     },
27137
27138     /**
27139      * Deselects a row.
27140      * @param {Number} row The index of the row to deselect
27141      */
27142     deselectRow : function(index, preventViewNotify)
27143     {
27144         if(this.locked) {
27145             return;
27146         }
27147         if(this.last == index){
27148             this.last = false;
27149         }
27150         if(this.lastActive == index){
27151             this.lastActive = false;
27152         }
27153         
27154         var r = this.grid.store.getAt(index);
27155         if (!r) {
27156             return;
27157         }
27158         
27159         this.selections.remove(r);
27160         //.console.log('deselectRow - record id :' + r.id);
27161         if(!preventViewNotify){
27162         
27163             var proxy = new Roo.Element(
27164                 this.grid.getRowDom(index)
27165             );
27166             proxy.removeClass('bg-info info');
27167         }
27168         this.fireEvent("rowdeselect", this, index);
27169         this.fireEvent("selectionchange", this);
27170     },
27171
27172     // private
27173     restoreLast : function(){
27174         if(this._last){
27175             this.last = this._last;
27176         }
27177     },
27178
27179     // private
27180     acceptsNav : function(row, col, cm){
27181         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27182     },
27183
27184     // private
27185     onEditorKey : function(field, e){
27186         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27187         if(k == e.TAB){
27188             e.stopEvent();
27189             ed.completeEdit();
27190             if(e.shiftKey){
27191                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27192             }else{
27193                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27194             }
27195         }else if(k == e.ENTER && !e.ctrlKey){
27196             e.stopEvent();
27197             ed.completeEdit();
27198             if(e.shiftKey){
27199                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27200             }else{
27201                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27202             }
27203         }else if(k == e.ESC){
27204             ed.cancelEdit();
27205         }
27206         if(newCell){
27207             g.startEditing(newCell[0], newCell[1]);
27208         }
27209     }
27210 });
27211 /*
27212  * Based on:
27213  * Ext JS Library 1.1.1
27214  * Copyright(c) 2006-2007, Ext JS, LLC.
27215  *
27216  * Originally Released Under LGPL - original licence link has changed is not relivant.
27217  *
27218  * Fork - LGPL
27219  * <script type="text/javascript">
27220  */
27221  
27222 /**
27223  * @class Roo.bootstrap.PagingToolbar
27224  * @extends Roo.bootstrap.NavSimplebar
27225  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27226  * @constructor
27227  * Create a new PagingToolbar
27228  * @param {Object} config The config object
27229  * @param {Roo.data.Store} store
27230  */
27231 Roo.bootstrap.PagingToolbar = function(config)
27232 {
27233     // old args format still supported... - xtype is prefered..
27234         // created from xtype...
27235     
27236     this.ds = config.dataSource;
27237     
27238     if (config.store && !this.ds) {
27239         this.store= Roo.factory(config.store, Roo.data);
27240         this.ds = this.store;
27241         this.ds.xmodule = this.xmodule || false;
27242     }
27243     
27244     this.toolbarItems = [];
27245     if (config.items) {
27246         this.toolbarItems = config.items;
27247     }
27248     
27249     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27250     
27251     this.cursor = 0;
27252     
27253     if (this.ds) { 
27254         this.bind(this.ds);
27255     }
27256     
27257     if (Roo.bootstrap.version == 4) {
27258         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27259     } else {
27260         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27261     }
27262     
27263 };
27264
27265 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27266     /**
27267      * @cfg {Roo.data.Store} dataSource
27268      * The underlying data store providing the paged data
27269      */
27270     /**
27271      * @cfg {String/HTMLElement/Element} container
27272      * container The id or element that will contain the toolbar
27273      */
27274     /**
27275      * @cfg {Boolean} displayInfo
27276      * True to display the displayMsg (defaults to false)
27277      */
27278     /**
27279      * @cfg {Number} pageSize
27280      * The number of records to display per page (defaults to 20)
27281      */
27282     pageSize: 20,
27283     /**
27284      * @cfg {String} displayMsg
27285      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27286      */
27287     displayMsg : 'Displaying {0} - {1} of {2}',
27288     /**
27289      * @cfg {String} emptyMsg
27290      * The message to display when no records are found (defaults to "No data to display")
27291      */
27292     emptyMsg : 'No data to display',
27293     /**
27294      * Customizable piece of the default paging text (defaults to "Page")
27295      * @type String
27296      */
27297     beforePageText : "Page",
27298     /**
27299      * Customizable piece of the default paging text (defaults to "of %0")
27300      * @type String
27301      */
27302     afterPageText : "of {0}",
27303     /**
27304      * Customizable piece of the default paging text (defaults to "First Page")
27305      * @type String
27306      */
27307     firstText : "First Page",
27308     /**
27309      * Customizable piece of the default paging text (defaults to "Previous Page")
27310      * @type String
27311      */
27312     prevText : "Previous Page",
27313     /**
27314      * Customizable piece of the default paging text (defaults to "Next Page")
27315      * @type String
27316      */
27317     nextText : "Next Page",
27318     /**
27319      * Customizable piece of the default paging text (defaults to "Last Page")
27320      * @type String
27321      */
27322     lastText : "Last Page",
27323     /**
27324      * Customizable piece of the default paging text (defaults to "Refresh")
27325      * @type String
27326      */
27327     refreshText : "Refresh",
27328
27329     buttons : false,
27330     // private
27331     onRender : function(ct, position) 
27332     {
27333         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27334         this.navgroup.parentId = this.id;
27335         this.navgroup.onRender(this.el, null);
27336         // add the buttons to the navgroup
27337         
27338         if(this.displayInfo){
27339             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27340             this.displayEl = this.el.select('.x-paging-info', true).first();
27341 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27342 //            this.displayEl = navel.el.select('span',true).first();
27343         }
27344         
27345         var _this = this;
27346         
27347         if(this.buttons){
27348             Roo.each(_this.buttons, function(e){ // this might need to use render????
27349                Roo.factory(e).render(_this.el);
27350             });
27351         }
27352             
27353         Roo.each(_this.toolbarItems, function(e) {
27354             _this.navgroup.addItem(e);
27355         });
27356         
27357         
27358         this.first = this.navgroup.addItem({
27359             tooltip: this.firstText,
27360             cls: "prev btn-outline-secondary",
27361             html : ' <i class="fa fa-step-backward"></i>',
27362             disabled: true,
27363             preventDefault: true,
27364             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27365         });
27366         
27367         this.prev =  this.navgroup.addItem({
27368             tooltip: this.prevText,
27369             cls: "prev btn-outline-secondary",
27370             html : ' <i class="fa fa-backward"></i>',
27371             disabled: true,
27372             preventDefault: true,
27373             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27374         });
27375     //this.addSeparator();
27376         
27377         
27378         var field = this.navgroup.addItem( {
27379             tagtype : 'span',
27380             cls : 'x-paging-position  btn-outline-secondary',
27381              disabled: true,
27382             html : this.beforePageText  +
27383                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27384                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27385          } ); //?? escaped?
27386         
27387         this.field = field.el.select('input', true).first();
27388         this.field.on("keydown", this.onPagingKeydown, this);
27389         this.field.on("focus", function(){this.dom.select();});
27390     
27391     
27392         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27393         //this.field.setHeight(18);
27394         //this.addSeparator();
27395         this.next = this.navgroup.addItem({
27396             tooltip: this.nextText,
27397             cls: "next btn-outline-secondary",
27398             html : ' <i class="fa fa-forward"></i>',
27399             disabled: true,
27400             preventDefault: true,
27401             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27402         });
27403         this.last = this.navgroup.addItem({
27404             tooltip: this.lastText,
27405             html : ' <i class="fa fa-step-forward"></i>',
27406             cls: "next btn-outline-secondary",
27407             disabled: true,
27408             preventDefault: true,
27409             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27410         });
27411     //this.addSeparator();
27412         this.loading = this.navgroup.addItem({
27413             tooltip: this.refreshText,
27414             cls: "btn-outline-secondary",
27415             html : ' <i class="fa fa-refresh"></i>',
27416             preventDefault: true,
27417             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27418         });
27419         
27420     },
27421
27422     // private
27423     updateInfo : function(){
27424         if(this.displayEl){
27425             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27426             var msg = count == 0 ?
27427                 this.emptyMsg :
27428                 String.format(
27429                     this.displayMsg,
27430                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27431                 );
27432             this.displayEl.update(msg);
27433         }
27434     },
27435
27436     // private
27437     onLoad : function(ds, r, o)
27438     {
27439         this.cursor = o.params && o.params.start ? o.params.start : 0;
27440         
27441         var d = this.getPageData(),
27442             ap = d.activePage,
27443             ps = d.pages;
27444         
27445         
27446         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27447         this.field.dom.value = ap;
27448         this.first.setDisabled(ap == 1);
27449         this.prev.setDisabled(ap == 1);
27450         this.next.setDisabled(ap == ps);
27451         this.last.setDisabled(ap == ps);
27452         this.loading.enable();
27453         this.updateInfo();
27454     },
27455
27456     // private
27457     getPageData : function(){
27458         var total = this.ds.getTotalCount();
27459         return {
27460             total : total,
27461             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27462             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27463         };
27464     },
27465
27466     // private
27467     onLoadError : function(){
27468         this.loading.enable();
27469     },
27470
27471     // private
27472     onPagingKeydown : function(e){
27473         var k = e.getKey();
27474         var d = this.getPageData();
27475         if(k == e.RETURN){
27476             var v = this.field.dom.value, pageNum;
27477             if(!v || isNaN(pageNum = parseInt(v, 10))){
27478                 this.field.dom.value = d.activePage;
27479                 return;
27480             }
27481             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27482             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27483             e.stopEvent();
27484         }
27485         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))
27486         {
27487           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27488           this.field.dom.value = pageNum;
27489           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27490           e.stopEvent();
27491         }
27492         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27493         {
27494           var v = this.field.dom.value, pageNum; 
27495           var increment = (e.shiftKey) ? 10 : 1;
27496           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27497                 increment *= -1;
27498           }
27499           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27500             this.field.dom.value = d.activePage;
27501             return;
27502           }
27503           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27504           {
27505             this.field.dom.value = parseInt(v, 10) + increment;
27506             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27507             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27508           }
27509           e.stopEvent();
27510         }
27511     },
27512
27513     // private
27514     beforeLoad : function(){
27515         if(this.loading){
27516             this.loading.disable();
27517         }
27518     },
27519
27520     // private
27521     onClick : function(which){
27522         
27523         var ds = this.ds;
27524         if (!ds) {
27525             return;
27526         }
27527         
27528         switch(which){
27529             case "first":
27530                 ds.load({params:{start: 0, limit: this.pageSize}});
27531             break;
27532             case "prev":
27533                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27534             break;
27535             case "next":
27536                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27537             break;
27538             case "last":
27539                 var total = ds.getTotalCount();
27540                 var extra = total % this.pageSize;
27541                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27542                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27543             break;
27544             case "refresh":
27545                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27546             break;
27547         }
27548     },
27549
27550     /**
27551      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27552      * @param {Roo.data.Store} store The data store to unbind
27553      */
27554     unbind : function(ds){
27555         ds.un("beforeload", this.beforeLoad, this);
27556         ds.un("load", this.onLoad, this);
27557         ds.un("loadexception", this.onLoadError, this);
27558         ds.un("remove", this.updateInfo, this);
27559         ds.un("add", this.updateInfo, this);
27560         this.ds = undefined;
27561     },
27562
27563     /**
27564      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27565      * @param {Roo.data.Store} store The data store to bind
27566      */
27567     bind : function(ds){
27568         ds.on("beforeload", this.beforeLoad, this);
27569         ds.on("load", this.onLoad, this);
27570         ds.on("loadexception", this.onLoadError, this);
27571         ds.on("remove", this.updateInfo, this);
27572         ds.on("add", this.updateInfo, this);
27573         this.ds = ds;
27574     }
27575 });/*
27576  * - LGPL
27577  *
27578  * element
27579  * 
27580  */
27581
27582 /**
27583  * @class Roo.bootstrap.MessageBar
27584  * @extends Roo.bootstrap.Component
27585  * Bootstrap MessageBar class
27586  * @cfg {String} html contents of the MessageBar
27587  * @cfg {String} weight (info | success | warning | danger) default info
27588  * @cfg {String} beforeClass insert the bar before the given class
27589  * @cfg {Boolean} closable (true | false) default false
27590  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27591  * 
27592  * @constructor
27593  * Create a new Element
27594  * @param {Object} config The config object
27595  */
27596
27597 Roo.bootstrap.MessageBar = function(config){
27598     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27599 };
27600
27601 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27602     
27603     html: '',
27604     weight: 'info',
27605     closable: false,
27606     fixed: false,
27607     beforeClass: 'bootstrap-sticky-wrap',
27608     
27609     getAutoCreate : function(){
27610         
27611         var cfg = {
27612             tag: 'div',
27613             cls: 'alert alert-dismissable alert-' + this.weight,
27614             cn: [
27615                 {
27616                     tag: 'span',
27617                     cls: 'message',
27618                     html: this.html || ''
27619                 }
27620             ]
27621         };
27622         
27623         if(this.fixed){
27624             cfg.cls += ' alert-messages-fixed';
27625         }
27626         
27627         if(this.closable){
27628             cfg.cn.push({
27629                 tag: 'button',
27630                 cls: 'close',
27631                 html: 'x'
27632             });
27633         }
27634         
27635         return cfg;
27636     },
27637     
27638     onRender : function(ct, position)
27639     {
27640         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27641         
27642         if(!this.el){
27643             var cfg = Roo.apply({},  this.getAutoCreate());
27644             cfg.id = Roo.id();
27645             
27646             if (this.cls) {
27647                 cfg.cls += ' ' + this.cls;
27648             }
27649             if (this.style) {
27650                 cfg.style = this.style;
27651             }
27652             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27653             
27654             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27655         }
27656         
27657         this.el.select('>button.close').on('click', this.hide, this);
27658         
27659     },
27660     
27661     show : function()
27662     {
27663         if (!this.rendered) {
27664             this.render();
27665         }
27666         
27667         this.el.show();
27668         
27669         this.fireEvent('show', this);
27670         
27671     },
27672     
27673     hide : function()
27674     {
27675         if (!this.rendered) {
27676             this.render();
27677         }
27678         
27679         this.el.hide();
27680         
27681         this.fireEvent('hide', this);
27682     },
27683     
27684     update : function()
27685     {
27686 //        var e = this.el.dom.firstChild;
27687 //        
27688 //        if(this.closable){
27689 //            e = e.nextSibling;
27690 //        }
27691 //        
27692 //        e.data = this.html || '';
27693
27694         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27695     }
27696    
27697 });
27698
27699  
27700
27701      /*
27702  * - LGPL
27703  *
27704  * Graph
27705  * 
27706  */
27707
27708
27709 /**
27710  * @class Roo.bootstrap.Graph
27711  * @extends Roo.bootstrap.Component
27712  * Bootstrap Graph class
27713 > Prameters
27714  -sm {number} sm 4
27715  -md {number} md 5
27716  @cfg {String} graphtype  bar | vbar | pie
27717  @cfg {number} g_x coodinator | centre x (pie)
27718  @cfg {number} g_y coodinator | centre y (pie)
27719  @cfg {number} g_r radius (pie)
27720  @cfg {number} g_height height of the chart (respected by all elements in the set)
27721  @cfg {number} g_width width of the chart (respected by all elements in the set)
27722  @cfg {Object} title The title of the chart
27723     
27724  -{Array}  values
27725  -opts (object) options for the chart 
27726      o {
27727      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27728      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27729      o vgutter (number)
27730      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.
27731      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27732      o to
27733      o stretch (boolean)
27734      o }
27735  -opts (object) options for the pie
27736      o{
27737      o cut
27738      o startAngle (number)
27739      o endAngle (number)
27740      } 
27741  *
27742  * @constructor
27743  * Create a new Input
27744  * @param {Object} config The config object
27745  */
27746
27747 Roo.bootstrap.Graph = function(config){
27748     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27749     
27750     this.addEvents({
27751         // img events
27752         /**
27753          * @event click
27754          * The img click event for the img.
27755          * @param {Roo.EventObject} e
27756          */
27757         "click" : true
27758     });
27759 };
27760
27761 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27762     
27763     sm: 4,
27764     md: 5,
27765     graphtype: 'bar',
27766     g_height: 250,
27767     g_width: 400,
27768     g_x: 50,
27769     g_y: 50,
27770     g_r: 30,
27771     opts:{
27772         //g_colors: this.colors,
27773         g_type: 'soft',
27774         g_gutter: '20%'
27775
27776     },
27777     title : false,
27778
27779     getAutoCreate : function(){
27780         
27781         var cfg = {
27782             tag: 'div',
27783             html : null
27784         };
27785         
27786         
27787         return  cfg;
27788     },
27789
27790     onRender : function(ct,position){
27791         
27792         
27793         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27794         
27795         if (typeof(Raphael) == 'undefined') {
27796             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27797             return;
27798         }
27799         
27800         this.raphael = Raphael(this.el.dom);
27801         
27802                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27803                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27804                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27805                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27806                 /*
27807                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27808                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27809                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27810                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27811                 
27812                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27813                 r.barchart(330, 10, 300, 220, data1);
27814                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27815                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27816                 */
27817                 
27818                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27819                 // r.barchart(30, 30, 560, 250,  xdata, {
27820                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27821                 //     axis : "0 0 1 1",
27822                 //     axisxlabels :  xdata
27823                 //     //yvalues : cols,
27824                    
27825                 // });
27826 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27827 //        
27828 //        this.load(null,xdata,{
27829 //                axis : "0 0 1 1",
27830 //                axisxlabels :  xdata
27831 //                });
27832
27833     },
27834
27835     load : function(graphtype,xdata,opts)
27836     {
27837         this.raphael.clear();
27838         if(!graphtype) {
27839             graphtype = this.graphtype;
27840         }
27841         if(!opts){
27842             opts = this.opts;
27843         }
27844         var r = this.raphael,
27845             fin = function () {
27846                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27847             },
27848             fout = function () {
27849                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27850             },
27851             pfin = function() {
27852                 this.sector.stop();
27853                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27854
27855                 if (this.label) {
27856                     this.label[0].stop();
27857                     this.label[0].attr({ r: 7.5 });
27858                     this.label[1].attr({ "font-weight": 800 });
27859                 }
27860             },
27861             pfout = function() {
27862                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27863
27864                 if (this.label) {
27865                     this.label[0].animate({ r: 5 }, 500, "bounce");
27866                     this.label[1].attr({ "font-weight": 400 });
27867                 }
27868             };
27869
27870         switch(graphtype){
27871             case 'bar':
27872                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27873                 break;
27874             case 'hbar':
27875                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27876                 break;
27877             case 'pie':
27878 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27879 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27880 //            
27881                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27882                 
27883                 break;
27884
27885         }
27886         
27887         if(this.title){
27888             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27889         }
27890         
27891     },
27892     
27893     setTitle: function(o)
27894     {
27895         this.title = o;
27896     },
27897     
27898     initEvents: function() {
27899         
27900         if(!this.href){
27901             this.el.on('click', this.onClick, this);
27902         }
27903     },
27904     
27905     onClick : function(e)
27906     {
27907         Roo.log('img onclick');
27908         this.fireEvent('click', this, e);
27909     }
27910    
27911 });
27912
27913  
27914 /*
27915  * - LGPL
27916  *
27917  * numberBox
27918  * 
27919  */
27920 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27921
27922 /**
27923  * @class Roo.bootstrap.dash.NumberBox
27924  * @extends Roo.bootstrap.Component
27925  * Bootstrap NumberBox class
27926  * @cfg {String} headline Box headline
27927  * @cfg {String} content Box content
27928  * @cfg {String} icon Box icon
27929  * @cfg {String} footer Footer text
27930  * @cfg {String} fhref Footer href
27931  * 
27932  * @constructor
27933  * Create a new NumberBox
27934  * @param {Object} config The config object
27935  */
27936
27937
27938 Roo.bootstrap.dash.NumberBox = function(config){
27939     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27940     
27941 };
27942
27943 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27944     
27945     headline : '',
27946     content : '',
27947     icon : '',
27948     footer : '',
27949     fhref : '',
27950     ficon : '',
27951     
27952     getAutoCreate : function(){
27953         
27954         var cfg = {
27955             tag : 'div',
27956             cls : 'small-box ',
27957             cn : [
27958                 {
27959                     tag : 'div',
27960                     cls : 'inner',
27961                     cn :[
27962                         {
27963                             tag : 'h3',
27964                             cls : 'roo-headline',
27965                             html : this.headline
27966                         },
27967                         {
27968                             tag : 'p',
27969                             cls : 'roo-content',
27970                             html : this.content
27971                         }
27972                     ]
27973                 }
27974             ]
27975         };
27976         
27977         if(this.icon){
27978             cfg.cn.push({
27979                 tag : 'div',
27980                 cls : 'icon',
27981                 cn :[
27982                     {
27983                         tag : 'i',
27984                         cls : 'ion ' + this.icon
27985                     }
27986                 ]
27987             });
27988         }
27989         
27990         if(this.footer){
27991             var footer = {
27992                 tag : 'a',
27993                 cls : 'small-box-footer',
27994                 href : this.fhref || '#',
27995                 html : this.footer
27996             };
27997             
27998             cfg.cn.push(footer);
27999             
28000         }
28001         
28002         return  cfg;
28003     },
28004
28005     onRender : function(ct,position){
28006         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28007
28008
28009        
28010                 
28011     },
28012
28013     setHeadline: function (value)
28014     {
28015         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28016     },
28017     
28018     setFooter: function (value, href)
28019     {
28020         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28021         
28022         if(href){
28023             this.el.select('a.small-box-footer',true).first().attr('href', href);
28024         }
28025         
28026     },
28027
28028     setContent: function (value)
28029     {
28030         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28031     },
28032
28033     initEvents: function() 
28034     {   
28035         
28036     }
28037     
28038 });
28039
28040  
28041 /*
28042  * - LGPL
28043  *
28044  * TabBox
28045  * 
28046  */
28047 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28048
28049 /**
28050  * @class Roo.bootstrap.dash.TabBox
28051  * @extends Roo.bootstrap.Component
28052  * Bootstrap TabBox class
28053  * @cfg {String} title Title of the TabBox
28054  * @cfg {String} icon Icon of the TabBox
28055  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28056  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28057  * 
28058  * @constructor
28059  * Create a new TabBox
28060  * @param {Object} config The config object
28061  */
28062
28063
28064 Roo.bootstrap.dash.TabBox = function(config){
28065     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28066     this.addEvents({
28067         // raw events
28068         /**
28069          * @event addpane
28070          * When a pane is added
28071          * @param {Roo.bootstrap.dash.TabPane} pane
28072          */
28073         "addpane" : true,
28074         /**
28075          * @event activatepane
28076          * When a pane is activated
28077          * @param {Roo.bootstrap.dash.TabPane} pane
28078          */
28079         "activatepane" : true
28080         
28081          
28082     });
28083     
28084     this.panes = [];
28085 };
28086
28087 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28088
28089     title : '',
28090     icon : false,
28091     showtabs : true,
28092     tabScrollable : false,
28093     
28094     getChildContainer : function()
28095     {
28096         return this.el.select('.tab-content', true).first();
28097     },
28098     
28099     getAutoCreate : function(){
28100         
28101         var header = {
28102             tag: 'li',
28103             cls: 'pull-left header',
28104             html: this.title,
28105             cn : []
28106         };
28107         
28108         if(this.icon){
28109             header.cn.push({
28110                 tag: 'i',
28111                 cls: 'fa ' + this.icon
28112             });
28113         }
28114         
28115         var h = {
28116             tag: 'ul',
28117             cls: 'nav nav-tabs pull-right',
28118             cn: [
28119                 header
28120             ]
28121         };
28122         
28123         if(this.tabScrollable){
28124             h = {
28125                 tag: 'div',
28126                 cls: 'tab-header',
28127                 cn: [
28128                     {
28129                         tag: 'ul',
28130                         cls: 'nav nav-tabs pull-right',
28131                         cn: [
28132                             header
28133                         ]
28134                     }
28135                 ]
28136             };
28137         }
28138         
28139         var cfg = {
28140             tag: 'div',
28141             cls: 'nav-tabs-custom',
28142             cn: [
28143                 h,
28144                 {
28145                     tag: 'div',
28146                     cls: 'tab-content no-padding',
28147                     cn: []
28148                 }
28149             ]
28150         };
28151
28152         return  cfg;
28153     },
28154     initEvents : function()
28155     {
28156         //Roo.log('add add pane handler');
28157         this.on('addpane', this.onAddPane, this);
28158     },
28159      /**
28160      * Updates the box title
28161      * @param {String} html to set the title to.
28162      */
28163     setTitle : function(value)
28164     {
28165         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28166     },
28167     onAddPane : function(pane)
28168     {
28169         this.panes.push(pane);
28170         //Roo.log('addpane');
28171         //Roo.log(pane);
28172         // tabs are rendere left to right..
28173         if(!this.showtabs){
28174             return;
28175         }
28176         
28177         var ctr = this.el.select('.nav-tabs', true).first();
28178          
28179          
28180         var existing = ctr.select('.nav-tab',true);
28181         var qty = existing.getCount();;
28182         
28183         
28184         var tab = ctr.createChild({
28185             tag : 'li',
28186             cls : 'nav-tab' + (qty ? '' : ' active'),
28187             cn : [
28188                 {
28189                     tag : 'a',
28190                     href:'#',
28191                     html : pane.title
28192                 }
28193             ]
28194         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28195         pane.tab = tab;
28196         
28197         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28198         if (!qty) {
28199             pane.el.addClass('active');
28200         }
28201         
28202                 
28203     },
28204     onTabClick : function(ev,un,ob,pane)
28205     {
28206         //Roo.log('tab - prev default');
28207         ev.preventDefault();
28208         
28209         
28210         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28211         pane.tab.addClass('active');
28212         //Roo.log(pane.title);
28213         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28214         // technically we should have a deactivate event.. but maybe add later.
28215         // and it should not de-activate the selected tab...
28216         this.fireEvent('activatepane', pane);
28217         pane.el.addClass('active');
28218         pane.fireEvent('activate');
28219         
28220         
28221     },
28222     
28223     getActivePane : function()
28224     {
28225         var r = false;
28226         Roo.each(this.panes, function(p) {
28227             if(p.el.hasClass('active')){
28228                 r = p;
28229                 return false;
28230             }
28231             
28232             return;
28233         });
28234         
28235         return r;
28236     }
28237     
28238     
28239 });
28240
28241  
28242 /*
28243  * - LGPL
28244  *
28245  * Tab pane
28246  * 
28247  */
28248 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28249 /**
28250  * @class Roo.bootstrap.TabPane
28251  * @extends Roo.bootstrap.Component
28252  * Bootstrap TabPane class
28253  * @cfg {Boolean} active (false | true) Default false
28254  * @cfg {String} title title of panel
28255
28256  * 
28257  * @constructor
28258  * Create a new TabPane
28259  * @param {Object} config The config object
28260  */
28261
28262 Roo.bootstrap.dash.TabPane = function(config){
28263     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28264     
28265     this.addEvents({
28266         // raw events
28267         /**
28268          * @event activate
28269          * When a pane is activated
28270          * @param {Roo.bootstrap.dash.TabPane} pane
28271          */
28272         "activate" : true
28273          
28274     });
28275 };
28276
28277 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28278     
28279     active : false,
28280     title : '',
28281     
28282     // the tabBox that this is attached to.
28283     tab : false,
28284      
28285     getAutoCreate : function() 
28286     {
28287         var cfg = {
28288             tag: 'div',
28289             cls: 'tab-pane'
28290         };
28291         
28292         if(this.active){
28293             cfg.cls += ' active';
28294         }
28295         
28296         return cfg;
28297     },
28298     initEvents  : function()
28299     {
28300         //Roo.log('trigger add pane handler');
28301         this.parent().fireEvent('addpane', this)
28302     },
28303     
28304      /**
28305      * Updates the tab title 
28306      * @param {String} html to set the title to.
28307      */
28308     setTitle: function(str)
28309     {
28310         if (!this.tab) {
28311             return;
28312         }
28313         this.title = str;
28314         this.tab.select('a', true).first().dom.innerHTML = str;
28315         
28316     }
28317     
28318     
28319     
28320 });
28321
28322  
28323
28324
28325  /*
28326  * - LGPL
28327  *
28328  * menu
28329  * 
28330  */
28331 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28332
28333 /**
28334  * @class Roo.bootstrap.menu.Menu
28335  * @extends Roo.bootstrap.Component
28336  * Bootstrap Menu class - container for Menu
28337  * @cfg {String} html Text of the menu
28338  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28339  * @cfg {String} icon Font awesome icon
28340  * @cfg {String} pos Menu align to (top | bottom) default bottom
28341  * 
28342  * 
28343  * @constructor
28344  * Create a new Menu
28345  * @param {Object} config The config object
28346  */
28347
28348
28349 Roo.bootstrap.menu.Menu = function(config){
28350     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28351     
28352     this.addEvents({
28353         /**
28354          * @event beforeshow
28355          * Fires before this menu is displayed
28356          * @param {Roo.bootstrap.menu.Menu} this
28357          */
28358         beforeshow : true,
28359         /**
28360          * @event beforehide
28361          * Fires before this menu is hidden
28362          * @param {Roo.bootstrap.menu.Menu} this
28363          */
28364         beforehide : true,
28365         /**
28366          * @event show
28367          * Fires after this menu is displayed
28368          * @param {Roo.bootstrap.menu.Menu} this
28369          */
28370         show : true,
28371         /**
28372          * @event hide
28373          * Fires after this menu is hidden
28374          * @param {Roo.bootstrap.menu.Menu} this
28375          */
28376         hide : true,
28377         /**
28378          * @event click
28379          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28380          * @param {Roo.bootstrap.menu.Menu} this
28381          * @param {Roo.EventObject} e
28382          */
28383         click : true
28384     });
28385     
28386 };
28387
28388 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28389     
28390     submenu : false,
28391     html : '',
28392     weight : 'default',
28393     icon : false,
28394     pos : 'bottom',
28395     
28396     
28397     getChildContainer : function() {
28398         if(this.isSubMenu){
28399             return this.el;
28400         }
28401         
28402         return this.el.select('ul.dropdown-menu', true).first();  
28403     },
28404     
28405     getAutoCreate : function()
28406     {
28407         var text = [
28408             {
28409                 tag : 'span',
28410                 cls : 'roo-menu-text',
28411                 html : this.html
28412             }
28413         ];
28414         
28415         if(this.icon){
28416             text.unshift({
28417                 tag : 'i',
28418                 cls : 'fa ' + this.icon
28419             })
28420         }
28421         
28422         
28423         var cfg = {
28424             tag : 'div',
28425             cls : 'btn-group',
28426             cn : [
28427                 {
28428                     tag : 'button',
28429                     cls : 'dropdown-button btn btn-' + this.weight,
28430                     cn : text
28431                 },
28432                 {
28433                     tag : 'button',
28434                     cls : 'dropdown-toggle btn btn-' + this.weight,
28435                     cn : [
28436                         {
28437                             tag : 'span',
28438                             cls : 'caret'
28439                         }
28440                     ]
28441                 },
28442                 {
28443                     tag : 'ul',
28444                     cls : 'dropdown-menu'
28445                 }
28446             ]
28447             
28448         };
28449         
28450         if(this.pos == 'top'){
28451             cfg.cls += ' dropup';
28452         }
28453         
28454         if(this.isSubMenu){
28455             cfg = {
28456                 tag : 'ul',
28457                 cls : 'dropdown-menu'
28458             }
28459         }
28460         
28461         return cfg;
28462     },
28463     
28464     onRender : function(ct, position)
28465     {
28466         this.isSubMenu = ct.hasClass('dropdown-submenu');
28467         
28468         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28469     },
28470     
28471     initEvents : function() 
28472     {
28473         if(this.isSubMenu){
28474             return;
28475         }
28476         
28477         this.hidden = true;
28478         
28479         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28480         this.triggerEl.on('click', this.onTriggerPress, this);
28481         
28482         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28483         this.buttonEl.on('click', this.onClick, this);
28484         
28485     },
28486     
28487     list : function()
28488     {
28489         if(this.isSubMenu){
28490             return this.el;
28491         }
28492         
28493         return this.el.select('ul.dropdown-menu', true).first();
28494     },
28495     
28496     onClick : function(e)
28497     {
28498         this.fireEvent("click", this, e);
28499     },
28500     
28501     onTriggerPress  : function(e)
28502     {   
28503         if (this.isVisible()) {
28504             this.hide();
28505         } else {
28506             this.show();
28507         }
28508     },
28509     
28510     isVisible : function(){
28511         return !this.hidden;
28512     },
28513     
28514     show : function()
28515     {
28516         this.fireEvent("beforeshow", this);
28517         
28518         this.hidden = false;
28519         this.el.addClass('open');
28520         
28521         Roo.get(document).on("mouseup", this.onMouseUp, this);
28522         
28523         this.fireEvent("show", this);
28524         
28525         
28526     },
28527     
28528     hide : function()
28529     {
28530         this.fireEvent("beforehide", this);
28531         
28532         this.hidden = true;
28533         this.el.removeClass('open');
28534         
28535         Roo.get(document).un("mouseup", this.onMouseUp);
28536         
28537         this.fireEvent("hide", this);
28538     },
28539     
28540     onMouseUp : function()
28541     {
28542         this.hide();
28543     }
28544     
28545 });
28546
28547  
28548  /*
28549  * - LGPL
28550  *
28551  * menu item
28552  * 
28553  */
28554 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28555
28556 /**
28557  * @class Roo.bootstrap.menu.Item
28558  * @extends Roo.bootstrap.Component
28559  * Bootstrap MenuItem class
28560  * @cfg {Boolean} submenu (true | false) default false
28561  * @cfg {String} html text of the item
28562  * @cfg {String} href the link
28563  * @cfg {Boolean} disable (true | false) default false
28564  * @cfg {Boolean} preventDefault (true | false) default true
28565  * @cfg {String} icon Font awesome icon
28566  * @cfg {String} pos Submenu align to (left | right) default right 
28567  * 
28568  * 
28569  * @constructor
28570  * Create a new Item
28571  * @param {Object} config The config object
28572  */
28573
28574
28575 Roo.bootstrap.menu.Item = function(config){
28576     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28577     this.addEvents({
28578         /**
28579          * @event mouseover
28580          * Fires when the mouse is hovering over this menu
28581          * @param {Roo.bootstrap.menu.Item} this
28582          * @param {Roo.EventObject} e
28583          */
28584         mouseover : true,
28585         /**
28586          * @event mouseout
28587          * Fires when the mouse exits this menu
28588          * @param {Roo.bootstrap.menu.Item} this
28589          * @param {Roo.EventObject} e
28590          */
28591         mouseout : true,
28592         // raw events
28593         /**
28594          * @event click
28595          * The raw click event for the entire grid.
28596          * @param {Roo.EventObject} e
28597          */
28598         click : true
28599     });
28600 };
28601
28602 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28603     
28604     submenu : false,
28605     href : '',
28606     html : '',
28607     preventDefault: true,
28608     disable : false,
28609     icon : false,
28610     pos : 'right',
28611     
28612     getAutoCreate : function()
28613     {
28614         var text = [
28615             {
28616                 tag : 'span',
28617                 cls : 'roo-menu-item-text',
28618                 html : this.html
28619             }
28620         ];
28621         
28622         if(this.icon){
28623             text.unshift({
28624                 tag : 'i',
28625                 cls : 'fa ' + this.icon
28626             })
28627         }
28628         
28629         var cfg = {
28630             tag : 'li',
28631             cn : [
28632                 {
28633                     tag : 'a',
28634                     href : this.href || '#',
28635                     cn : text
28636                 }
28637             ]
28638         };
28639         
28640         if(this.disable){
28641             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28642         }
28643         
28644         if(this.submenu){
28645             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28646             
28647             if(this.pos == 'left'){
28648                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28649             }
28650         }
28651         
28652         return cfg;
28653     },
28654     
28655     initEvents : function() 
28656     {
28657         this.el.on('mouseover', this.onMouseOver, this);
28658         this.el.on('mouseout', this.onMouseOut, this);
28659         
28660         this.el.select('a', true).first().on('click', this.onClick, this);
28661         
28662     },
28663     
28664     onClick : function(e)
28665     {
28666         if(this.preventDefault){
28667             e.preventDefault();
28668         }
28669         
28670         this.fireEvent("click", this, e);
28671     },
28672     
28673     onMouseOver : function(e)
28674     {
28675         if(this.submenu && this.pos == 'left'){
28676             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28677         }
28678         
28679         this.fireEvent("mouseover", this, e);
28680     },
28681     
28682     onMouseOut : function(e)
28683     {
28684         this.fireEvent("mouseout", this, e);
28685     }
28686 });
28687
28688  
28689
28690  /*
28691  * - LGPL
28692  *
28693  * menu separator
28694  * 
28695  */
28696 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28697
28698 /**
28699  * @class Roo.bootstrap.menu.Separator
28700  * @extends Roo.bootstrap.Component
28701  * Bootstrap Separator class
28702  * 
28703  * @constructor
28704  * Create a new Separator
28705  * @param {Object} config The config object
28706  */
28707
28708
28709 Roo.bootstrap.menu.Separator = function(config){
28710     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28711 };
28712
28713 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28714     
28715     getAutoCreate : function(){
28716         var cfg = {
28717             tag : 'li',
28718             cls: 'divider'
28719         };
28720         
28721         return cfg;
28722     }
28723    
28724 });
28725
28726  
28727
28728  /*
28729  * - LGPL
28730  *
28731  * Tooltip
28732  * 
28733  */
28734
28735 /**
28736  * @class Roo.bootstrap.Tooltip
28737  * Bootstrap Tooltip class
28738  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28739  * to determine which dom element triggers the tooltip.
28740  * 
28741  * It needs to add support for additional attributes like tooltip-position
28742  * 
28743  * @constructor
28744  * Create a new Toolti
28745  * @param {Object} config The config object
28746  */
28747
28748 Roo.bootstrap.Tooltip = function(config){
28749     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28750     
28751     this.alignment = Roo.bootstrap.Tooltip.alignment;
28752     
28753     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28754         this.alignment = config.alignment;
28755     }
28756     
28757 };
28758
28759 Roo.apply(Roo.bootstrap.Tooltip, {
28760     /**
28761      * @function init initialize tooltip monitoring.
28762      * @static
28763      */
28764     currentEl : false,
28765     currentTip : false,
28766     currentRegion : false,
28767     
28768     //  init : delay?
28769     
28770     init : function()
28771     {
28772         Roo.get(document).on('mouseover', this.enter ,this);
28773         Roo.get(document).on('mouseout', this.leave, this);
28774          
28775         
28776         this.currentTip = new Roo.bootstrap.Tooltip();
28777     },
28778     
28779     enter : function(ev)
28780     {
28781         var dom = ev.getTarget();
28782         
28783         //Roo.log(['enter',dom]);
28784         var el = Roo.fly(dom);
28785         if (this.currentEl) {
28786             //Roo.log(dom);
28787             //Roo.log(this.currentEl);
28788             //Roo.log(this.currentEl.contains(dom));
28789             if (this.currentEl == el) {
28790                 return;
28791             }
28792             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28793                 return;
28794             }
28795
28796         }
28797         
28798         if (this.currentTip.el) {
28799             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28800         }    
28801         //Roo.log(ev);
28802         
28803         if(!el || el.dom == document){
28804             return;
28805         }
28806         
28807         var bindEl = el;
28808         
28809         // you can not look for children, as if el is the body.. then everythign is the child..
28810         if (!el.attr('tooltip')) { //
28811             if (!el.select("[tooltip]").elements.length) {
28812                 return;
28813             }
28814             // is the mouse over this child...?
28815             bindEl = el.select("[tooltip]").first();
28816             var xy = ev.getXY();
28817             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28818                 //Roo.log("not in region.");
28819                 return;
28820             }
28821             //Roo.log("child element over..");
28822             
28823         }
28824         this.currentEl = bindEl;
28825         this.currentTip.bind(bindEl);
28826         this.currentRegion = Roo.lib.Region.getRegion(dom);
28827         this.currentTip.enter();
28828         
28829     },
28830     leave : function(ev)
28831     {
28832         var dom = ev.getTarget();
28833         //Roo.log(['leave',dom]);
28834         if (!this.currentEl) {
28835             return;
28836         }
28837         
28838         
28839         if (dom != this.currentEl.dom) {
28840             return;
28841         }
28842         var xy = ev.getXY();
28843         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28844             return;
28845         }
28846         // only activate leave if mouse cursor is outside... bounding box..
28847         
28848         
28849         
28850         
28851         if (this.currentTip) {
28852             this.currentTip.leave();
28853         }
28854         //Roo.log('clear currentEl');
28855         this.currentEl = false;
28856         
28857         
28858     },
28859     alignment : {
28860         'left' : ['r-l', [-2,0], 'right'],
28861         'right' : ['l-r', [2,0], 'left'],
28862         'bottom' : ['t-b', [0,2], 'top'],
28863         'top' : [ 'b-t', [0,-2], 'bottom']
28864     }
28865     
28866 });
28867
28868
28869 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28870     
28871     
28872     bindEl : false,
28873     
28874     delay : null, // can be { show : 300 , hide: 500}
28875     
28876     timeout : null,
28877     
28878     hoverState : null, //???
28879     
28880     placement : 'bottom', 
28881     
28882     alignment : false,
28883     
28884     getAutoCreate : function(){
28885     
28886         var cfg = {
28887            cls : 'tooltip',   
28888            role : 'tooltip',
28889            cn : [
28890                 {
28891                     cls : 'tooltip-arrow arrow'
28892                 },
28893                 {
28894                     cls : 'tooltip-inner'
28895                 }
28896            ]
28897         };
28898         
28899         return cfg;
28900     },
28901     bind : function(el)
28902     {
28903         this.bindEl = el;
28904     },
28905     
28906     initEvents : function()
28907     {
28908         this.arrowEl = this.el.select('.arrow', true).first();
28909         this.innerEl = this.el.select('.tooltip-inner', true).first();
28910     },
28911     
28912     enter : function () {
28913        
28914         if (this.timeout != null) {
28915             clearTimeout(this.timeout);
28916         }
28917         
28918         this.hoverState = 'in';
28919          //Roo.log("enter - show");
28920         if (!this.delay || !this.delay.show) {
28921             this.show();
28922             return;
28923         }
28924         var _t = this;
28925         this.timeout = setTimeout(function () {
28926             if (_t.hoverState == 'in') {
28927                 _t.show();
28928             }
28929         }, this.delay.show);
28930     },
28931     leave : function()
28932     {
28933         clearTimeout(this.timeout);
28934     
28935         this.hoverState = 'out';
28936          if (!this.delay || !this.delay.hide) {
28937             this.hide();
28938             return;
28939         }
28940        
28941         var _t = this;
28942         this.timeout = setTimeout(function () {
28943             //Roo.log("leave - timeout");
28944             
28945             if (_t.hoverState == 'out') {
28946                 _t.hide();
28947                 Roo.bootstrap.Tooltip.currentEl = false;
28948             }
28949         }, delay);
28950     },
28951     
28952     show : function (msg)
28953     {
28954         if (!this.el) {
28955             this.render(document.body);
28956         }
28957         // set content.
28958         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28959         
28960         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28961         
28962         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28963         
28964         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28965                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28966         
28967         var placement = typeof this.placement == 'function' ?
28968             this.placement.call(this, this.el, on_el) :
28969             this.placement;
28970             
28971         var autoToken = /\s?auto?\s?/i;
28972         var autoPlace = autoToken.test(placement);
28973         if (autoPlace) {
28974             placement = placement.replace(autoToken, '') || 'top';
28975         }
28976         
28977         //this.el.detach()
28978         //this.el.setXY([0,0]);
28979         this.el.show();
28980         //this.el.dom.style.display='block';
28981         
28982         //this.el.appendTo(on_el);
28983         
28984         var p = this.getPosition();
28985         var box = this.el.getBox();
28986         
28987         if (autoPlace) {
28988             // fixme..
28989         }
28990         
28991         var align = this.alignment[placement];
28992         
28993         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28994         
28995         if(placement == 'top' || placement == 'bottom'){
28996             if(xy[0] < 0){
28997                 placement = 'right';
28998             }
28999             
29000             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29001                 placement = 'left';
29002             }
29003             
29004             var scroll = Roo.select('body', true).first().getScroll();
29005             
29006             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29007                 placement = 'top';
29008             }
29009             
29010             align = this.alignment[placement];
29011             
29012             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29013             
29014         }
29015         
29016         this.el.alignTo(this.bindEl, align[0],align[1]);
29017         //var arrow = this.el.select('.arrow',true).first();
29018         //arrow.set(align[2], 
29019         
29020         this.el.addClass(placement);
29021         this.el.addClass("bs-tooltip-"+ placement);
29022         
29023         this.el.addClass('in fade show');
29024         
29025         this.hoverState = null;
29026         
29027         if (this.el.hasClass('fade')) {
29028             // fade it?
29029         }
29030         
29031         
29032         
29033         
29034         
29035     },
29036     hide : function()
29037     {
29038          
29039         if (!this.el) {
29040             return;
29041         }
29042         //this.el.setXY([0,0]);
29043         this.el.removeClass(['show', 'in']);
29044         //this.el.hide();
29045         
29046     }
29047     
29048 });
29049  
29050
29051  /*
29052  * - LGPL
29053  *
29054  * Location Picker
29055  * 
29056  */
29057
29058 /**
29059  * @class Roo.bootstrap.LocationPicker
29060  * @extends Roo.bootstrap.Component
29061  * Bootstrap LocationPicker class
29062  * @cfg {Number} latitude Position when init default 0
29063  * @cfg {Number} longitude Position when init default 0
29064  * @cfg {Number} zoom default 15
29065  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29066  * @cfg {Boolean} mapTypeControl default false
29067  * @cfg {Boolean} disableDoubleClickZoom default false
29068  * @cfg {Boolean} scrollwheel default true
29069  * @cfg {Boolean} streetViewControl default false
29070  * @cfg {Number} radius default 0
29071  * @cfg {String} locationName
29072  * @cfg {Boolean} draggable default true
29073  * @cfg {Boolean} enableAutocomplete default false
29074  * @cfg {Boolean} enableReverseGeocode default true
29075  * @cfg {String} markerTitle
29076  * 
29077  * @constructor
29078  * Create a new LocationPicker
29079  * @param {Object} config The config object
29080  */
29081
29082
29083 Roo.bootstrap.LocationPicker = function(config){
29084     
29085     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29086     
29087     this.addEvents({
29088         /**
29089          * @event initial
29090          * Fires when the picker initialized.
29091          * @param {Roo.bootstrap.LocationPicker} this
29092          * @param {Google Location} location
29093          */
29094         initial : true,
29095         /**
29096          * @event positionchanged
29097          * Fires when the picker position changed.
29098          * @param {Roo.bootstrap.LocationPicker} this
29099          * @param {Google Location} location
29100          */
29101         positionchanged : true,
29102         /**
29103          * @event resize
29104          * Fires when the map resize.
29105          * @param {Roo.bootstrap.LocationPicker} this
29106          */
29107         resize : true,
29108         /**
29109          * @event show
29110          * Fires when the map show.
29111          * @param {Roo.bootstrap.LocationPicker} this
29112          */
29113         show : true,
29114         /**
29115          * @event hide
29116          * Fires when the map hide.
29117          * @param {Roo.bootstrap.LocationPicker} this
29118          */
29119         hide : true,
29120         /**
29121          * @event mapClick
29122          * Fires when click the map.
29123          * @param {Roo.bootstrap.LocationPicker} this
29124          * @param {Map event} e
29125          */
29126         mapClick : true,
29127         /**
29128          * @event mapRightClick
29129          * Fires when right click the map.
29130          * @param {Roo.bootstrap.LocationPicker} this
29131          * @param {Map event} e
29132          */
29133         mapRightClick : true,
29134         /**
29135          * @event markerClick
29136          * Fires when click the marker.
29137          * @param {Roo.bootstrap.LocationPicker} this
29138          * @param {Map event} e
29139          */
29140         markerClick : true,
29141         /**
29142          * @event markerRightClick
29143          * Fires when right click the marker.
29144          * @param {Roo.bootstrap.LocationPicker} this
29145          * @param {Map event} e
29146          */
29147         markerRightClick : true,
29148         /**
29149          * @event OverlayViewDraw
29150          * Fires when OverlayView Draw
29151          * @param {Roo.bootstrap.LocationPicker} this
29152          */
29153         OverlayViewDraw : true,
29154         /**
29155          * @event OverlayViewOnAdd
29156          * Fires when OverlayView Draw
29157          * @param {Roo.bootstrap.LocationPicker} this
29158          */
29159         OverlayViewOnAdd : true,
29160         /**
29161          * @event OverlayViewOnRemove
29162          * Fires when OverlayView Draw
29163          * @param {Roo.bootstrap.LocationPicker} this
29164          */
29165         OverlayViewOnRemove : true,
29166         /**
29167          * @event OverlayViewShow
29168          * Fires when OverlayView Draw
29169          * @param {Roo.bootstrap.LocationPicker} this
29170          * @param {Pixel} cpx
29171          */
29172         OverlayViewShow : true,
29173         /**
29174          * @event OverlayViewHide
29175          * Fires when OverlayView Draw
29176          * @param {Roo.bootstrap.LocationPicker} this
29177          */
29178         OverlayViewHide : true,
29179         /**
29180          * @event loadexception
29181          * Fires when load google lib failed.
29182          * @param {Roo.bootstrap.LocationPicker} this
29183          */
29184         loadexception : true
29185     });
29186         
29187 };
29188
29189 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29190     
29191     gMapContext: false,
29192     
29193     latitude: 0,
29194     longitude: 0,
29195     zoom: 15,
29196     mapTypeId: false,
29197     mapTypeControl: false,
29198     disableDoubleClickZoom: false,
29199     scrollwheel: true,
29200     streetViewControl: false,
29201     radius: 0,
29202     locationName: '',
29203     draggable: true,
29204     enableAutocomplete: false,
29205     enableReverseGeocode: true,
29206     markerTitle: '',
29207     
29208     getAutoCreate: function()
29209     {
29210
29211         var cfg = {
29212             tag: 'div',
29213             cls: 'roo-location-picker'
29214         };
29215         
29216         return cfg
29217     },
29218     
29219     initEvents: function(ct, position)
29220     {       
29221         if(!this.el.getWidth() || this.isApplied()){
29222             return;
29223         }
29224         
29225         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29226         
29227         this.initial();
29228     },
29229     
29230     initial: function()
29231     {
29232         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29233             this.fireEvent('loadexception', this);
29234             return;
29235         }
29236         
29237         if(!this.mapTypeId){
29238             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29239         }
29240         
29241         this.gMapContext = this.GMapContext();
29242         
29243         this.initOverlayView();
29244         
29245         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29246         
29247         var _this = this;
29248                 
29249         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29250             _this.setPosition(_this.gMapContext.marker.position);
29251         });
29252         
29253         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29254             _this.fireEvent('mapClick', this, event);
29255             
29256         });
29257
29258         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29259             _this.fireEvent('mapRightClick', this, event);
29260             
29261         });
29262         
29263         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29264             _this.fireEvent('markerClick', this, event);
29265             
29266         });
29267
29268         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29269             _this.fireEvent('markerRightClick', this, event);
29270             
29271         });
29272         
29273         this.setPosition(this.gMapContext.location);
29274         
29275         this.fireEvent('initial', this, this.gMapContext.location);
29276     },
29277     
29278     initOverlayView: function()
29279     {
29280         var _this = this;
29281         
29282         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29283             
29284             draw: function()
29285             {
29286                 _this.fireEvent('OverlayViewDraw', _this);
29287             },
29288             
29289             onAdd: function()
29290             {
29291                 _this.fireEvent('OverlayViewOnAdd', _this);
29292             },
29293             
29294             onRemove: function()
29295             {
29296                 _this.fireEvent('OverlayViewOnRemove', _this);
29297             },
29298             
29299             show: function(cpx)
29300             {
29301                 _this.fireEvent('OverlayViewShow', _this, cpx);
29302             },
29303             
29304             hide: function()
29305             {
29306                 _this.fireEvent('OverlayViewHide', _this);
29307             }
29308             
29309         });
29310     },
29311     
29312     fromLatLngToContainerPixel: function(event)
29313     {
29314         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29315     },
29316     
29317     isApplied: function() 
29318     {
29319         return this.getGmapContext() == false ? false : true;
29320     },
29321     
29322     getGmapContext: function() 
29323     {
29324         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29325     },
29326     
29327     GMapContext: function() 
29328     {
29329         var position = new google.maps.LatLng(this.latitude, this.longitude);
29330         
29331         var _map = new google.maps.Map(this.el.dom, {
29332             center: position,
29333             zoom: this.zoom,
29334             mapTypeId: this.mapTypeId,
29335             mapTypeControl: this.mapTypeControl,
29336             disableDoubleClickZoom: this.disableDoubleClickZoom,
29337             scrollwheel: this.scrollwheel,
29338             streetViewControl: this.streetViewControl,
29339             locationName: this.locationName,
29340             draggable: this.draggable,
29341             enableAutocomplete: this.enableAutocomplete,
29342             enableReverseGeocode: this.enableReverseGeocode
29343         });
29344         
29345         var _marker = new google.maps.Marker({
29346             position: position,
29347             map: _map,
29348             title: this.markerTitle,
29349             draggable: this.draggable
29350         });
29351         
29352         return {
29353             map: _map,
29354             marker: _marker,
29355             circle: null,
29356             location: position,
29357             radius: this.radius,
29358             locationName: this.locationName,
29359             addressComponents: {
29360                 formatted_address: null,
29361                 addressLine1: null,
29362                 addressLine2: null,
29363                 streetName: null,
29364                 streetNumber: null,
29365                 city: null,
29366                 district: null,
29367                 state: null,
29368                 stateOrProvince: null
29369             },
29370             settings: this,
29371             domContainer: this.el.dom,
29372             geodecoder: new google.maps.Geocoder()
29373         };
29374     },
29375     
29376     drawCircle: function(center, radius, options) 
29377     {
29378         if (this.gMapContext.circle != null) {
29379             this.gMapContext.circle.setMap(null);
29380         }
29381         if (radius > 0) {
29382             radius *= 1;
29383             options = Roo.apply({}, options, {
29384                 strokeColor: "#0000FF",
29385                 strokeOpacity: .35,
29386                 strokeWeight: 2,
29387                 fillColor: "#0000FF",
29388                 fillOpacity: .2
29389             });
29390             
29391             options.map = this.gMapContext.map;
29392             options.radius = radius;
29393             options.center = center;
29394             this.gMapContext.circle = new google.maps.Circle(options);
29395             return this.gMapContext.circle;
29396         }
29397         
29398         return null;
29399     },
29400     
29401     setPosition: function(location) 
29402     {
29403         this.gMapContext.location = location;
29404         this.gMapContext.marker.setPosition(location);
29405         this.gMapContext.map.panTo(location);
29406         this.drawCircle(location, this.gMapContext.radius, {});
29407         
29408         var _this = this;
29409         
29410         if (this.gMapContext.settings.enableReverseGeocode) {
29411             this.gMapContext.geodecoder.geocode({
29412                 latLng: this.gMapContext.location
29413             }, function(results, status) {
29414                 
29415                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29416                     _this.gMapContext.locationName = results[0].formatted_address;
29417                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29418                     
29419                     _this.fireEvent('positionchanged', this, location);
29420                 }
29421             });
29422             
29423             return;
29424         }
29425         
29426         this.fireEvent('positionchanged', this, location);
29427     },
29428     
29429     resize: function()
29430     {
29431         google.maps.event.trigger(this.gMapContext.map, "resize");
29432         
29433         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29434         
29435         this.fireEvent('resize', this);
29436     },
29437     
29438     setPositionByLatLng: function(latitude, longitude)
29439     {
29440         this.setPosition(new google.maps.LatLng(latitude, longitude));
29441     },
29442     
29443     getCurrentPosition: function() 
29444     {
29445         return {
29446             latitude: this.gMapContext.location.lat(),
29447             longitude: this.gMapContext.location.lng()
29448         };
29449     },
29450     
29451     getAddressName: function() 
29452     {
29453         return this.gMapContext.locationName;
29454     },
29455     
29456     getAddressComponents: function() 
29457     {
29458         return this.gMapContext.addressComponents;
29459     },
29460     
29461     address_component_from_google_geocode: function(address_components) 
29462     {
29463         var result = {};
29464         
29465         for (var i = 0; i < address_components.length; i++) {
29466             var component = address_components[i];
29467             if (component.types.indexOf("postal_code") >= 0) {
29468                 result.postalCode = component.short_name;
29469             } else if (component.types.indexOf("street_number") >= 0) {
29470                 result.streetNumber = component.short_name;
29471             } else if (component.types.indexOf("route") >= 0) {
29472                 result.streetName = component.short_name;
29473             } else if (component.types.indexOf("neighborhood") >= 0) {
29474                 result.city = component.short_name;
29475             } else if (component.types.indexOf("locality") >= 0) {
29476                 result.city = component.short_name;
29477             } else if (component.types.indexOf("sublocality") >= 0) {
29478                 result.district = component.short_name;
29479             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29480                 result.stateOrProvince = component.short_name;
29481             } else if (component.types.indexOf("country") >= 0) {
29482                 result.country = component.short_name;
29483             }
29484         }
29485         
29486         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29487         result.addressLine2 = "";
29488         return result;
29489     },
29490     
29491     setZoomLevel: function(zoom)
29492     {
29493         this.gMapContext.map.setZoom(zoom);
29494     },
29495     
29496     show: function()
29497     {
29498         if(!this.el){
29499             return;
29500         }
29501         
29502         this.el.show();
29503         
29504         this.resize();
29505         
29506         this.fireEvent('show', this);
29507     },
29508     
29509     hide: function()
29510     {
29511         if(!this.el){
29512             return;
29513         }
29514         
29515         this.el.hide();
29516         
29517         this.fireEvent('hide', this);
29518     }
29519     
29520 });
29521
29522 Roo.apply(Roo.bootstrap.LocationPicker, {
29523     
29524     OverlayView : function(map, options)
29525     {
29526         options = options || {};
29527         
29528         this.setMap(map);
29529     }
29530     
29531     
29532 });/**
29533  * @class Roo.bootstrap.Alert
29534  * @extends Roo.bootstrap.Component
29535  * Bootstrap Alert class - shows an alert area box
29536  * eg
29537  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29538   Enter a valid email address
29539 </div>
29540  * @licence LGPL
29541  * @cfg {String} title The title of alert
29542  * @cfg {String} html The content of alert
29543  * @cfg {String} weight (  success | info | warning | danger )
29544  * @cfg {String} faicon font-awesomeicon
29545  * 
29546  * @constructor
29547  * Create a new alert
29548  * @param {Object} config The config object
29549  */
29550
29551
29552 Roo.bootstrap.Alert = function(config){
29553     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29554     
29555 };
29556
29557 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29558     
29559     title: '',
29560     html: '',
29561     weight: false,
29562     faicon: false,
29563     
29564     getAutoCreate : function()
29565     {
29566         
29567         var cfg = {
29568             tag : 'div',
29569             cls : 'alert',
29570             cn : [
29571                 {
29572                     tag : 'i',
29573                     cls : 'roo-alert-icon'
29574                     
29575                 },
29576                 {
29577                     tag : 'b',
29578                     cls : 'roo-alert-title',
29579                     html : this.title
29580                 },
29581                 {
29582                     tag : 'span',
29583                     cls : 'roo-alert-text',
29584                     html : this.html
29585                 }
29586             ]
29587         };
29588         
29589         if(this.faicon){
29590             cfg.cn[0].cls += ' fa ' + this.faicon;
29591         }
29592         
29593         if(this.weight){
29594             cfg.cls += ' alert-' + this.weight;
29595         }
29596         
29597         return cfg;
29598     },
29599     
29600     initEvents: function() 
29601     {
29602         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29603     },
29604     
29605     setTitle : function(str)
29606     {
29607         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29608     },
29609     
29610     setText : function(str)
29611     {
29612         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29613     },
29614     
29615     setWeight : function(weight)
29616     {
29617         if(this.weight){
29618             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29619         }
29620         
29621         this.weight = weight;
29622         
29623         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29624     },
29625     
29626     setIcon : function(icon)
29627     {
29628         if(this.faicon){
29629             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29630         }
29631         
29632         this.faicon = icon;
29633         
29634         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29635     },
29636     
29637     hide: function() 
29638     {
29639         this.el.hide();   
29640     },
29641     
29642     show: function() 
29643     {  
29644         this.el.show();   
29645     }
29646     
29647 });
29648
29649  
29650 /*
29651 * Licence: LGPL
29652 */
29653
29654 /**
29655  * @class Roo.bootstrap.UploadCropbox
29656  * @extends Roo.bootstrap.Component
29657  * Bootstrap UploadCropbox class
29658  * @cfg {String} emptyText show when image has been loaded
29659  * @cfg {String} rotateNotify show when image too small to rotate
29660  * @cfg {Number} errorTimeout default 3000
29661  * @cfg {Number} minWidth default 300
29662  * @cfg {Number} minHeight default 300
29663  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29664  * @cfg {Boolean} isDocument (true|false) default false
29665  * @cfg {String} url action url
29666  * @cfg {String} paramName default 'imageUpload'
29667  * @cfg {String} method default POST
29668  * @cfg {Boolean} loadMask (true|false) default true
29669  * @cfg {Boolean} loadingText default 'Loading...'
29670  * 
29671  * @constructor
29672  * Create a new UploadCropbox
29673  * @param {Object} config The config object
29674  */
29675
29676 Roo.bootstrap.UploadCropbox = function(config){
29677     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29678     
29679     this.addEvents({
29680         /**
29681          * @event beforeselectfile
29682          * Fire before select file
29683          * @param {Roo.bootstrap.UploadCropbox} this
29684          */
29685         "beforeselectfile" : true,
29686         /**
29687          * @event initial
29688          * Fire after initEvent
29689          * @param {Roo.bootstrap.UploadCropbox} this
29690          */
29691         "initial" : true,
29692         /**
29693          * @event crop
29694          * Fire after initEvent
29695          * @param {Roo.bootstrap.UploadCropbox} this
29696          * @param {String} data
29697          */
29698         "crop" : true,
29699         /**
29700          * @event prepare
29701          * Fire when preparing the file data
29702          * @param {Roo.bootstrap.UploadCropbox} this
29703          * @param {Object} file
29704          */
29705         "prepare" : true,
29706         /**
29707          * @event exception
29708          * Fire when get exception
29709          * @param {Roo.bootstrap.UploadCropbox} this
29710          * @param {XMLHttpRequest} xhr
29711          */
29712         "exception" : true,
29713         /**
29714          * @event beforeloadcanvas
29715          * Fire before load the canvas
29716          * @param {Roo.bootstrap.UploadCropbox} this
29717          * @param {String} src
29718          */
29719         "beforeloadcanvas" : true,
29720         /**
29721          * @event trash
29722          * Fire when trash image
29723          * @param {Roo.bootstrap.UploadCropbox} this
29724          */
29725         "trash" : true,
29726         /**
29727          * @event download
29728          * Fire when download the image
29729          * @param {Roo.bootstrap.UploadCropbox} this
29730          */
29731         "download" : true,
29732         /**
29733          * @event footerbuttonclick
29734          * Fire when footerbuttonclick
29735          * @param {Roo.bootstrap.UploadCropbox} this
29736          * @param {String} type
29737          */
29738         "footerbuttonclick" : true,
29739         /**
29740          * @event resize
29741          * Fire when resize
29742          * @param {Roo.bootstrap.UploadCropbox} this
29743          */
29744         "resize" : true,
29745         /**
29746          * @event rotate
29747          * Fire when rotate the image
29748          * @param {Roo.bootstrap.UploadCropbox} this
29749          * @param {String} pos
29750          */
29751         "rotate" : true,
29752         /**
29753          * @event inspect
29754          * Fire when inspect the file
29755          * @param {Roo.bootstrap.UploadCropbox} this
29756          * @param {Object} file
29757          */
29758         "inspect" : true,
29759         /**
29760          * @event upload
29761          * Fire when xhr upload the file
29762          * @param {Roo.bootstrap.UploadCropbox} this
29763          * @param {Object} data
29764          */
29765         "upload" : true,
29766         /**
29767          * @event arrange
29768          * Fire when arrange the file data
29769          * @param {Roo.bootstrap.UploadCropbox} this
29770          * @param {Object} formData
29771          */
29772         "arrange" : true
29773     });
29774     
29775     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29776 };
29777
29778 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29779     
29780     emptyText : 'Click to upload image',
29781     rotateNotify : 'Image is too small to rotate',
29782     errorTimeout : 3000,
29783     scale : 0,
29784     baseScale : 1,
29785     rotate : 0,
29786     dragable : false,
29787     pinching : false,
29788     mouseX : 0,
29789     mouseY : 0,
29790     cropData : false,
29791     minWidth : 300,
29792     minHeight : 300,
29793     file : false,
29794     exif : {},
29795     baseRotate : 1,
29796     cropType : 'image/jpeg',
29797     buttons : false,
29798     canvasLoaded : false,
29799     isDocument : false,
29800     method : 'POST',
29801     paramName : 'imageUpload',
29802     loadMask : true,
29803     loadingText : 'Loading...',
29804     maskEl : false,
29805     
29806     getAutoCreate : function()
29807     {
29808         var cfg = {
29809             tag : 'div',
29810             cls : 'roo-upload-cropbox',
29811             cn : [
29812                 {
29813                     tag : 'input',
29814                     cls : 'roo-upload-cropbox-selector',
29815                     type : 'file'
29816                 },
29817                 {
29818                     tag : 'div',
29819                     cls : 'roo-upload-cropbox-body',
29820                     style : 'cursor:pointer',
29821                     cn : [
29822                         {
29823                             tag : 'div',
29824                             cls : 'roo-upload-cropbox-preview'
29825                         },
29826                         {
29827                             tag : 'div',
29828                             cls : 'roo-upload-cropbox-thumb'
29829                         },
29830                         {
29831                             tag : 'div',
29832                             cls : 'roo-upload-cropbox-empty-notify',
29833                             html : this.emptyText
29834                         },
29835                         {
29836                             tag : 'div',
29837                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29838                             html : this.rotateNotify
29839                         }
29840                     ]
29841                 },
29842                 {
29843                     tag : 'div',
29844                     cls : 'roo-upload-cropbox-footer',
29845                     cn : {
29846                         tag : 'div',
29847                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29848                         cn : []
29849                     }
29850                 }
29851             ]
29852         };
29853         
29854         return cfg;
29855     },
29856     
29857     onRender : function(ct, position)
29858     {
29859         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29860         
29861         if (this.buttons.length) {
29862             
29863             Roo.each(this.buttons, function(bb) {
29864                 
29865                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29866                 
29867                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29868                 
29869             }, this);
29870         }
29871         
29872         if(this.loadMask){
29873             this.maskEl = this.el;
29874         }
29875     },
29876     
29877     initEvents : function()
29878     {
29879         this.urlAPI = (window.createObjectURL && window) || 
29880                                 (window.URL && URL.revokeObjectURL && URL) || 
29881                                 (window.webkitURL && webkitURL);
29882                         
29883         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29884         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29885         
29886         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29887         this.selectorEl.hide();
29888         
29889         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29890         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29891         
29892         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29893         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29894         this.thumbEl.hide();
29895         
29896         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29897         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29898         
29899         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29900         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29901         this.errorEl.hide();
29902         
29903         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29904         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29905         this.footerEl.hide();
29906         
29907         this.setThumbBoxSize();
29908         
29909         this.bind();
29910         
29911         this.resize();
29912         
29913         this.fireEvent('initial', this);
29914     },
29915
29916     bind : function()
29917     {
29918         var _this = this;
29919         
29920         window.addEventListener("resize", function() { _this.resize(); } );
29921         
29922         this.bodyEl.on('click', this.beforeSelectFile, this);
29923         
29924         if(Roo.isTouch){
29925             this.bodyEl.on('touchstart', this.onTouchStart, this);
29926             this.bodyEl.on('touchmove', this.onTouchMove, this);
29927             this.bodyEl.on('touchend', this.onTouchEnd, this);
29928         }
29929         
29930         if(!Roo.isTouch){
29931             this.bodyEl.on('mousedown', this.onMouseDown, this);
29932             this.bodyEl.on('mousemove', this.onMouseMove, this);
29933             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29934             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29935             Roo.get(document).on('mouseup', this.onMouseUp, this);
29936         }
29937         
29938         this.selectorEl.on('change', this.onFileSelected, this);
29939     },
29940     
29941     reset : function()
29942     {    
29943         this.scale = 0;
29944         this.baseScale = 1;
29945         this.rotate = 0;
29946         this.baseRotate = 1;
29947         this.dragable = false;
29948         this.pinching = false;
29949         this.mouseX = 0;
29950         this.mouseY = 0;
29951         this.cropData = false;
29952         this.notifyEl.dom.innerHTML = this.emptyText;
29953         
29954         this.selectorEl.dom.value = '';
29955         
29956     },
29957     
29958     resize : function()
29959     {
29960         if(this.fireEvent('resize', this) != false){
29961             this.setThumbBoxPosition();
29962             this.setCanvasPosition();
29963         }
29964     },
29965     
29966     onFooterButtonClick : function(e, el, o, type)
29967     {
29968         switch (type) {
29969             case 'rotate-left' :
29970                 this.onRotateLeft(e);
29971                 break;
29972             case 'rotate-right' :
29973                 this.onRotateRight(e);
29974                 break;
29975             case 'picture' :
29976                 this.beforeSelectFile(e);
29977                 break;
29978             case 'trash' :
29979                 this.trash(e);
29980                 break;
29981             case 'crop' :
29982                 this.crop(e);
29983                 break;
29984             case 'download' :
29985                 this.download(e);
29986                 break;
29987             default :
29988                 break;
29989         }
29990         
29991         this.fireEvent('footerbuttonclick', this, type);
29992     },
29993     
29994     beforeSelectFile : function(e)
29995     {
29996         e.preventDefault();
29997         
29998         if(this.fireEvent('beforeselectfile', this) != false){
29999             this.selectorEl.dom.click();
30000         }
30001     },
30002     
30003     onFileSelected : function(e)
30004     {
30005         e.preventDefault();
30006         
30007         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30008             return;
30009         }
30010         
30011         var file = this.selectorEl.dom.files[0];
30012         
30013         if(this.fireEvent('inspect', this, file) != false){
30014             this.prepare(file);
30015         }
30016         
30017     },
30018     
30019     trash : function(e)
30020     {
30021         this.fireEvent('trash', this);
30022     },
30023     
30024     download : function(e)
30025     {
30026         this.fireEvent('download', this);
30027     },
30028     
30029     loadCanvas : function(src)
30030     {   
30031         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30032             
30033             this.reset();
30034             
30035             this.imageEl = document.createElement('img');
30036             
30037             var _this = this;
30038             
30039             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30040             
30041             this.imageEl.src = src;
30042         }
30043     },
30044     
30045     onLoadCanvas : function()
30046     {   
30047         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30048         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30049         
30050         this.bodyEl.un('click', this.beforeSelectFile, this);
30051         
30052         this.notifyEl.hide();
30053         this.thumbEl.show();
30054         this.footerEl.show();
30055         
30056         this.baseRotateLevel();
30057         
30058         if(this.isDocument){
30059             this.setThumbBoxSize();
30060         }
30061         
30062         this.setThumbBoxPosition();
30063         
30064         this.baseScaleLevel();
30065         
30066         this.draw();
30067         
30068         this.resize();
30069         
30070         this.canvasLoaded = true;
30071         
30072         if(this.loadMask){
30073             this.maskEl.unmask();
30074         }
30075         
30076     },
30077     
30078     setCanvasPosition : function()
30079     {   
30080         if(!this.canvasEl){
30081             return;
30082         }
30083         
30084         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30085         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30086         
30087         this.previewEl.setLeft(pw);
30088         this.previewEl.setTop(ph);
30089         
30090     },
30091     
30092     onMouseDown : function(e)
30093     {   
30094         e.stopEvent();
30095         
30096         this.dragable = true;
30097         this.pinching = false;
30098         
30099         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30100             this.dragable = false;
30101             return;
30102         }
30103         
30104         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30105         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30106         
30107     },
30108     
30109     onMouseMove : function(e)
30110     {   
30111         e.stopEvent();
30112         
30113         if(!this.canvasLoaded){
30114             return;
30115         }
30116         
30117         if (!this.dragable){
30118             return;
30119         }
30120         
30121         var minX = Math.ceil(this.thumbEl.getLeft(true));
30122         var minY = Math.ceil(this.thumbEl.getTop(true));
30123         
30124         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30125         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30126         
30127         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30128         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30129         
30130         x = x - this.mouseX;
30131         y = y - this.mouseY;
30132         
30133         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30134         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30135         
30136         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30137         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30138         
30139         this.previewEl.setLeft(bgX);
30140         this.previewEl.setTop(bgY);
30141         
30142         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30143         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30144     },
30145     
30146     onMouseUp : function(e)
30147     {   
30148         e.stopEvent();
30149         
30150         this.dragable = false;
30151     },
30152     
30153     onMouseWheel : function(e)
30154     {   
30155         e.stopEvent();
30156         
30157         this.startScale = this.scale;
30158         
30159         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30160         
30161         if(!this.zoomable()){
30162             this.scale = this.startScale;
30163             return;
30164         }
30165         
30166         this.draw();
30167         
30168         return;
30169     },
30170     
30171     zoomable : function()
30172     {
30173         var minScale = this.thumbEl.getWidth() / this.minWidth;
30174         
30175         if(this.minWidth < this.minHeight){
30176             minScale = this.thumbEl.getHeight() / this.minHeight;
30177         }
30178         
30179         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30180         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30181         
30182         if(
30183                 this.isDocument &&
30184                 (this.rotate == 0 || this.rotate == 180) && 
30185                 (
30186                     width > this.imageEl.OriginWidth || 
30187                     height > this.imageEl.OriginHeight ||
30188                     (width < this.minWidth && height < this.minHeight)
30189                 )
30190         ){
30191             return false;
30192         }
30193         
30194         if(
30195                 this.isDocument &&
30196                 (this.rotate == 90 || this.rotate == 270) && 
30197                 (
30198                     width > this.imageEl.OriginWidth || 
30199                     height > this.imageEl.OriginHeight ||
30200                     (width < this.minHeight && height < this.minWidth)
30201                 )
30202         ){
30203             return false;
30204         }
30205         
30206         if(
30207                 !this.isDocument &&
30208                 (this.rotate == 0 || this.rotate == 180) && 
30209                 (
30210                     width < this.minWidth || 
30211                     width > this.imageEl.OriginWidth || 
30212                     height < this.minHeight || 
30213                     height > this.imageEl.OriginHeight
30214                 )
30215         ){
30216             return false;
30217         }
30218         
30219         if(
30220                 !this.isDocument &&
30221                 (this.rotate == 90 || this.rotate == 270) && 
30222                 (
30223                     width < this.minHeight || 
30224                     width > this.imageEl.OriginWidth || 
30225                     height < this.minWidth || 
30226                     height > this.imageEl.OriginHeight
30227                 )
30228         ){
30229             return false;
30230         }
30231         
30232         return true;
30233         
30234     },
30235     
30236     onRotateLeft : function(e)
30237     {   
30238         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30239             
30240             var minScale = this.thumbEl.getWidth() / this.minWidth;
30241             
30242             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30243             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30244             
30245             this.startScale = this.scale;
30246             
30247             while (this.getScaleLevel() < minScale){
30248             
30249                 this.scale = this.scale + 1;
30250                 
30251                 if(!this.zoomable()){
30252                     break;
30253                 }
30254                 
30255                 if(
30256                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30257                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30258                 ){
30259                     continue;
30260                 }
30261                 
30262                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30263
30264                 this.draw();
30265                 
30266                 return;
30267             }
30268             
30269             this.scale = this.startScale;
30270             
30271             this.onRotateFail();
30272             
30273             return false;
30274         }
30275         
30276         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30277
30278         if(this.isDocument){
30279             this.setThumbBoxSize();
30280             this.setThumbBoxPosition();
30281             this.setCanvasPosition();
30282         }
30283         
30284         this.draw();
30285         
30286         this.fireEvent('rotate', this, 'left');
30287         
30288     },
30289     
30290     onRotateRight : function(e)
30291     {
30292         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30293             
30294             var minScale = this.thumbEl.getWidth() / this.minWidth;
30295         
30296             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30297             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30298             
30299             this.startScale = this.scale;
30300             
30301             while (this.getScaleLevel() < minScale){
30302             
30303                 this.scale = this.scale + 1;
30304                 
30305                 if(!this.zoomable()){
30306                     break;
30307                 }
30308                 
30309                 if(
30310                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30311                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30312                 ){
30313                     continue;
30314                 }
30315                 
30316                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30317
30318                 this.draw();
30319                 
30320                 return;
30321             }
30322             
30323             this.scale = this.startScale;
30324             
30325             this.onRotateFail();
30326             
30327             return false;
30328         }
30329         
30330         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30331
30332         if(this.isDocument){
30333             this.setThumbBoxSize();
30334             this.setThumbBoxPosition();
30335             this.setCanvasPosition();
30336         }
30337         
30338         this.draw();
30339         
30340         this.fireEvent('rotate', this, 'right');
30341     },
30342     
30343     onRotateFail : function()
30344     {
30345         this.errorEl.show(true);
30346         
30347         var _this = this;
30348         
30349         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30350     },
30351     
30352     draw : function()
30353     {
30354         this.previewEl.dom.innerHTML = '';
30355         
30356         var canvasEl = document.createElement("canvas");
30357         
30358         var contextEl = canvasEl.getContext("2d");
30359         
30360         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30361         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30362         var center = this.imageEl.OriginWidth / 2;
30363         
30364         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30365             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30366             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30367             center = this.imageEl.OriginHeight / 2;
30368         }
30369         
30370         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30371         
30372         contextEl.translate(center, center);
30373         contextEl.rotate(this.rotate * Math.PI / 180);
30374
30375         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30376         
30377         this.canvasEl = document.createElement("canvas");
30378         
30379         this.contextEl = this.canvasEl.getContext("2d");
30380         
30381         switch (this.rotate) {
30382             case 0 :
30383                 
30384                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30385                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30386                 
30387                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30388                 
30389                 break;
30390             case 90 : 
30391                 
30392                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30393                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30394                 
30395                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30396                     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);
30397                     break;
30398                 }
30399                 
30400                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30401                 
30402                 break;
30403             case 180 :
30404                 
30405                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30406                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30407                 
30408                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30409                     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);
30410                     break;
30411                 }
30412                 
30413                 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);
30414                 
30415                 break;
30416             case 270 :
30417                 
30418                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30419                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30420         
30421                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30422                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30423                     break;
30424                 }
30425                 
30426                 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);
30427                 
30428                 break;
30429             default : 
30430                 break;
30431         }
30432         
30433         this.previewEl.appendChild(this.canvasEl);
30434         
30435         this.setCanvasPosition();
30436     },
30437     
30438     crop : function()
30439     {
30440         if(!this.canvasLoaded){
30441             return;
30442         }
30443         
30444         var imageCanvas = document.createElement("canvas");
30445         
30446         var imageContext = imageCanvas.getContext("2d");
30447         
30448         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30449         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30450         
30451         var center = imageCanvas.width / 2;
30452         
30453         imageContext.translate(center, center);
30454         
30455         imageContext.rotate(this.rotate * Math.PI / 180);
30456         
30457         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30458         
30459         var canvas = document.createElement("canvas");
30460         
30461         var context = canvas.getContext("2d");
30462                 
30463         canvas.width = this.minWidth;
30464         canvas.height = this.minHeight;
30465
30466         switch (this.rotate) {
30467             case 0 :
30468                 
30469                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30470                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30471                 
30472                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30473                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30474                 
30475                 var targetWidth = this.minWidth - 2 * x;
30476                 var targetHeight = this.minHeight - 2 * y;
30477                 
30478                 var scale = 1;
30479                 
30480                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30481                     scale = targetWidth / width;
30482                 }
30483                 
30484                 if(x > 0 && y == 0){
30485                     scale = targetHeight / height;
30486                 }
30487                 
30488                 if(x > 0 && y > 0){
30489                     scale = targetWidth / width;
30490                     
30491                     if(width < height){
30492                         scale = targetHeight / height;
30493                     }
30494                 }
30495                 
30496                 context.scale(scale, scale);
30497                 
30498                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30499                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30500
30501                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30502                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30503
30504                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30505                 
30506                 break;
30507             case 90 : 
30508                 
30509                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30510                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30511                 
30512                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30513                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30514                 
30515                 var targetWidth = this.minWidth - 2 * x;
30516                 var targetHeight = this.minHeight - 2 * y;
30517                 
30518                 var scale = 1;
30519                 
30520                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30521                     scale = targetWidth / width;
30522                 }
30523                 
30524                 if(x > 0 && y == 0){
30525                     scale = targetHeight / height;
30526                 }
30527                 
30528                 if(x > 0 && y > 0){
30529                     scale = targetWidth / width;
30530                     
30531                     if(width < height){
30532                         scale = targetHeight / height;
30533                     }
30534                 }
30535                 
30536                 context.scale(scale, scale);
30537                 
30538                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30539                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30540
30541                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30542                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30543                 
30544                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30545                 
30546                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30547                 
30548                 break;
30549             case 180 :
30550                 
30551                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30552                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30553                 
30554                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30555                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30556                 
30557                 var targetWidth = this.minWidth - 2 * x;
30558                 var targetHeight = this.minHeight - 2 * y;
30559                 
30560                 var scale = 1;
30561                 
30562                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30563                     scale = targetWidth / width;
30564                 }
30565                 
30566                 if(x > 0 && y == 0){
30567                     scale = targetHeight / height;
30568                 }
30569                 
30570                 if(x > 0 && y > 0){
30571                     scale = targetWidth / width;
30572                     
30573                     if(width < height){
30574                         scale = targetHeight / height;
30575                     }
30576                 }
30577                 
30578                 context.scale(scale, scale);
30579                 
30580                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30581                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30582
30583                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30584                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30585
30586                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30587                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30588                 
30589                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30590                 
30591                 break;
30592             case 270 :
30593                 
30594                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30595                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30596                 
30597                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30598                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30599                 
30600                 var targetWidth = this.minWidth - 2 * x;
30601                 var targetHeight = this.minHeight - 2 * y;
30602                 
30603                 var scale = 1;
30604                 
30605                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30606                     scale = targetWidth / width;
30607                 }
30608                 
30609                 if(x > 0 && y == 0){
30610                     scale = targetHeight / height;
30611                 }
30612                 
30613                 if(x > 0 && y > 0){
30614                     scale = targetWidth / width;
30615                     
30616                     if(width < height){
30617                         scale = targetHeight / height;
30618                     }
30619                 }
30620                 
30621                 context.scale(scale, scale);
30622                 
30623                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30624                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30625
30626                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30627                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30628                 
30629                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30630                 
30631                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30632                 
30633                 break;
30634             default : 
30635                 break;
30636         }
30637         
30638         this.cropData = canvas.toDataURL(this.cropType);
30639         
30640         if(this.fireEvent('crop', this, this.cropData) !== false){
30641             this.process(this.file, this.cropData);
30642         }
30643         
30644         return;
30645         
30646     },
30647     
30648     setThumbBoxSize : function()
30649     {
30650         var width, height;
30651         
30652         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30653             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30654             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30655             
30656             this.minWidth = width;
30657             this.minHeight = height;
30658             
30659             if(this.rotate == 90 || this.rotate == 270){
30660                 this.minWidth = height;
30661                 this.minHeight = width;
30662             }
30663         }
30664         
30665         height = 300;
30666         width = Math.ceil(this.minWidth * height / this.minHeight);
30667         
30668         if(this.minWidth > this.minHeight){
30669             width = 300;
30670             height = Math.ceil(this.minHeight * width / this.minWidth);
30671         }
30672         
30673         this.thumbEl.setStyle({
30674             width : width + 'px',
30675             height : height + 'px'
30676         });
30677
30678         return;
30679             
30680     },
30681     
30682     setThumbBoxPosition : function()
30683     {
30684         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30685         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30686         
30687         this.thumbEl.setLeft(x);
30688         this.thumbEl.setTop(y);
30689         
30690     },
30691     
30692     baseRotateLevel : function()
30693     {
30694         this.baseRotate = 1;
30695         
30696         if(
30697                 typeof(this.exif) != 'undefined' &&
30698                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30699                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30700         ){
30701             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30702         }
30703         
30704         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30705         
30706     },
30707     
30708     baseScaleLevel : function()
30709     {
30710         var width, height;
30711         
30712         if(this.isDocument){
30713             
30714             if(this.baseRotate == 6 || this.baseRotate == 8){
30715             
30716                 height = this.thumbEl.getHeight();
30717                 this.baseScale = height / this.imageEl.OriginWidth;
30718
30719                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30720                     width = this.thumbEl.getWidth();
30721                     this.baseScale = width / this.imageEl.OriginHeight;
30722                 }
30723
30724                 return;
30725             }
30726
30727             height = this.thumbEl.getHeight();
30728             this.baseScale = height / this.imageEl.OriginHeight;
30729
30730             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30731                 width = this.thumbEl.getWidth();
30732                 this.baseScale = width / this.imageEl.OriginWidth;
30733             }
30734
30735             return;
30736         }
30737         
30738         if(this.baseRotate == 6 || this.baseRotate == 8){
30739             
30740             width = this.thumbEl.getHeight();
30741             this.baseScale = width / this.imageEl.OriginHeight;
30742             
30743             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30744                 height = this.thumbEl.getWidth();
30745                 this.baseScale = height / this.imageEl.OriginHeight;
30746             }
30747             
30748             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30749                 height = this.thumbEl.getWidth();
30750                 this.baseScale = height / this.imageEl.OriginHeight;
30751                 
30752                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30753                     width = this.thumbEl.getHeight();
30754                     this.baseScale = width / this.imageEl.OriginWidth;
30755                 }
30756             }
30757             
30758             return;
30759         }
30760         
30761         width = this.thumbEl.getWidth();
30762         this.baseScale = width / this.imageEl.OriginWidth;
30763         
30764         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30765             height = this.thumbEl.getHeight();
30766             this.baseScale = height / this.imageEl.OriginHeight;
30767         }
30768         
30769         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30770             
30771             height = this.thumbEl.getHeight();
30772             this.baseScale = height / this.imageEl.OriginHeight;
30773             
30774             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30775                 width = this.thumbEl.getWidth();
30776                 this.baseScale = width / this.imageEl.OriginWidth;
30777             }
30778             
30779         }
30780         
30781         return;
30782     },
30783     
30784     getScaleLevel : function()
30785     {
30786         return this.baseScale * Math.pow(1.1, this.scale);
30787     },
30788     
30789     onTouchStart : function(e)
30790     {
30791         if(!this.canvasLoaded){
30792             this.beforeSelectFile(e);
30793             return;
30794         }
30795         
30796         var touches = e.browserEvent.touches;
30797         
30798         if(!touches){
30799             return;
30800         }
30801         
30802         if(touches.length == 1){
30803             this.onMouseDown(e);
30804             return;
30805         }
30806         
30807         if(touches.length != 2){
30808             return;
30809         }
30810         
30811         var coords = [];
30812         
30813         for(var i = 0, finger; finger = touches[i]; i++){
30814             coords.push(finger.pageX, finger.pageY);
30815         }
30816         
30817         var x = Math.pow(coords[0] - coords[2], 2);
30818         var y = Math.pow(coords[1] - coords[3], 2);
30819         
30820         this.startDistance = Math.sqrt(x + y);
30821         
30822         this.startScale = this.scale;
30823         
30824         this.pinching = true;
30825         this.dragable = false;
30826         
30827     },
30828     
30829     onTouchMove : function(e)
30830     {
30831         if(!this.pinching && !this.dragable){
30832             return;
30833         }
30834         
30835         var touches = e.browserEvent.touches;
30836         
30837         if(!touches){
30838             return;
30839         }
30840         
30841         if(this.dragable){
30842             this.onMouseMove(e);
30843             return;
30844         }
30845         
30846         var coords = [];
30847         
30848         for(var i = 0, finger; finger = touches[i]; i++){
30849             coords.push(finger.pageX, finger.pageY);
30850         }
30851         
30852         var x = Math.pow(coords[0] - coords[2], 2);
30853         var y = Math.pow(coords[1] - coords[3], 2);
30854         
30855         this.endDistance = Math.sqrt(x + y);
30856         
30857         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30858         
30859         if(!this.zoomable()){
30860             this.scale = this.startScale;
30861             return;
30862         }
30863         
30864         this.draw();
30865         
30866     },
30867     
30868     onTouchEnd : function(e)
30869     {
30870         this.pinching = false;
30871         this.dragable = false;
30872         
30873     },
30874     
30875     process : function(file, crop)
30876     {
30877         if(this.loadMask){
30878             this.maskEl.mask(this.loadingText);
30879         }
30880         
30881         this.xhr = new XMLHttpRequest();
30882         
30883         file.xhr = this.xhr;
30884
30885         this.xhr.open(this.method, this.url, true);
30886         
30887         var headers = {
30888             "Accept": "application/json",
30889             "Cache-Control": "no-cache",
30890             "X-Requested-With": "XMLHttpRequest"
30891         };
30892         
30893         for (var headerName in headers) {
30894             var headerValue = headers[headerName];
30895             if (headerValue) {
30896                 this.xhr.setRequestHeader(headerName, headerValue);
30897             }
30898         }
30899         
30900         var _this = this;
30901         
30902         this.xhr.onload = function()
30903         {
30904             _this.xhrOnLoad(_this.xhr);
30905         }
30906         
30907         this.xhr.onerror = function()
30908         {
30909             _this.xhrOnError(_this.xhr);
30910         }
30911         
30912         var formData = new FormData();
30913
30914         formData.append('returnHTML', 'NO');
30915         
30916         if(crop){
30917             formData.append('crop', crop);
30918         }
30919         
30920         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30921             formData.append(this.paramName, file, file.name);
30922         }
30923         
30924         if(typeof(file.filename) != 'undefined'){
30925             formData.append('filename', file.filename);
30926         }
30927         
30928         if(typeof(file.mimetype) != 'undefined'){
30929             formData.append('mimetype', file.mimetype);
30930         }
30931         
30932         if(this.fireEvent('arrange', this, formData) != false){
30933             this.xhr.send(formData);
30934         };
30935     },
30936     
30937     xhrOnLoad : function(xhr)
30938     {
30939         if(this.loadMask){
30940             this.maskEl.unmask();
30941         }
30942         
30943         if (xhr.readyState !== 4) {
30944             this.fireEvent('exception', this, xhr);
30945             return;
30946         }
30947
30948         var response = Roo.decode(xhr.responseText);
30949         
30950         if(!response.success){
30951             this.fireEvent('exception', this, xhr);
30952             return;
30953         }
30954         
30955         var response = Roo.decode(xhr.responseText);
30956         
30957         this.fireEvent('upload', this, response);
30958         
30959     },
30960     
30961     xhrOnError : function()
30962     {
30963         if(this.loadMask){
30964             this.maskEl.unmask();
30965         }
30966         
30967         Roo.log('xhr on error');
30968         
30969         var response = Roo.decode(xhr.responseText);
30970           
30971         Roo.log(response);
30972         
30973     },
30974     
30975     prepare : function(file)
30976     {   
30977         if(this.loadMask){
30978             this.maskEl.mask(this.loadingText);
30979         }
30980         
30981         this.file = false;
30982         this.exif = {};
30983         
30984         if(typeof(file) === 'string'){
30985             this.loadCanvas(file);
30986             return;
30987         }
30988         
30989         if(!file || !this.urlAPI){
30990             return;
30991         }
30992         
30993         this.file = file;
30994         this.cropType = file.type;
30995         
30996         var _this = this;
30997         
30998         if(this.fireEvent('prepare', this, this.file) != false){
30999             
31000             var reader = new FileReader();
31001             
31002             reader.onload = function (e) {
31003                 if (e.target.error) {
31004                     Roo.log(e.target.error);
31005                     return;
31006                 }
31007                 
31008                 var buffer = e.target.result,
31009                     dataView = new DataView(buffer),
31010                     offset = 2,
31011                     maxOffset = dataView.byteLength - 4,
31012                     markerBytes,
31013                     markerLength;
31014                 
31015                 if (dataView.getUint16(0) === 0xffd8) {
31016                     while (offset < maxOffset) {
31017                         markerBytes = dataView.getUint16(offset);
31018                         
31019                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31020                             markerLength = dataView.getUint16(offset + 2) + 2;
31021                             if (offset + markerLength > dataView.byteLength) {
31022                                 Roo.log('Invalid meta data: Invalid segment size.');
31023                                 break;
31024                             }
31025                             
31026                             if(markerBytes == 0xffe1){
31027                                 _this.parseExifData(
31028                                     dataView,
31029                                     offset,
31030                                     markerLength
31031                                 );
31032                             }
31033                             
31034                             offset += markerLength;
31035                             
31036                             continue;
31037                         }
31038                         
31039                         break;
31040                     }
31041                     
31042                 }
31043                 
31044                 var url = _this.urlAPI.createObjectURL(_this.file);
31045                 
31046                 _this.loadCanvas(url);
31047                 
31048                 return;
31049             }
31050             
31051             reader.readAsArrayBuffer(this.file);
31052             
31053         }
31054         
31055     },
31056     
31057     parseExifData : function(dataView, offset, length)
31058     {
31059         var tiffOffset = offset + 10,
31060             littleEndian,
31061             dirOffset;
31062     
31063         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31064             // No Exif data, might be XMP data instead
31065             return;
31066         }
31067         
31068         // Check for the ASCII code for "Exif" (0x45786966):
31069         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31070             // No Exif data, might be XMP data instead
31071             return;
31072         }
31073         if (tiffOffset + 8 > dataView.byteLength) {
31074             Roo.log('Invalid Exif data: Invalid segment size.');
31075             return;
31076         }
31077         // Check for the two null bytes:
31078         if (dataView.getUint16(offset + 8) !== 0x0000) {
31079             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31080             return;
31081         }
31082         // Check the byte alignment:
31083         switch (dataView.getUint16(tiffOffset)) {
31084         case 0x4949:
31085             littleEndian = true;
31086             break;
31087         case 0x4D4D:
31088             littleEndian = false;
31089             break;
31090         default:
31091             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31092             return;
31093         }
31094         // Check for the TIFF tag marker (0x002A):
31095         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31096             Roo.log('Invalid Exif data: Missing TIFF marker.');
31097             return;
31098         }
31099         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31100         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31101         
31102         this.parseExifTags(
31103             dataView,
31104             tiffOffset,
31105             tiffOffset + dirOffset,
31106             littleEndian
31107         );
31108     },
31109     
31110     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31111     {
31112         var tagsNumber,
31113             dirEndOffset,
31114             i;
31115         if (dirOffset + 6 > dataView.byteLength) {
31116             Roo.log('Invalid Exif data: Invalid directory offset.');
31117             return;
31118         }
31119         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31120         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31121         if (dirEndOffset + 4 > dataView.byteLength) {
31122             Roo.log('Invalid Exif data: Invalid directory size.');
31123             return;
31124         }
31125         for (i = 0; i < tagsNumber; i += 1) {
31126             this.parseExifTag(
31127                 dataView,
31128                 tiffOffset,
31129                 dirOffset + 2 + 12 * i, // tag offset
31130                 littleEndian
31131             );
31132         }
31133         // Return the offset to the next directory:
31134         return dataView.getUint32(dirEndOffset, littleEndian);
31135     },
31136     
31137     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31138     {
31139         var tag = dataView.getUint16(offset, littleEndian);
31140         
31141         this.exif[tag] = this.getExifValue(
31142             dataView,
31143             tiffOffset,
31144             offset,
31145             dataView.getUint16(offset + 2, littleEndian), // tag type
31146             dataView.getUint32(offset + 4, littleEndian), // tag length
31147             littleEndian
31148         );
31149     },
31150     
31151     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31152     {
31153         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31154             tagSize,
31155             dataOffset,
31156             values,
31157             i,
31158             str,
31159             c;
31160     
31161         if (!tagType) {
31162             Roo.log('Invalid Exif data: Invalid tag type.');
31163             return;
31164         }
31165         
31166         tagSize = tagType.size * length;
31167         // Determine if the value is contained in the dataOffset bytes,
31168         // or if the value at the dataOffset is a pointer to the actual data:
31169         dataOffset = tagSize > 4 ?
31170                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31171         if (dataOffset + tagSize > dataView.byteLength) {
31172             Roo.log('Invalid Exif data: Invalid data offset.');
31173             return;
31174         }
31175         if (length === 1) {
31176             return tagType.getValue(dataView, dataOffset, littleEndian);
31177         }
31178         values = [];
31179         for (i = 0; i < length; i += 1) {
31180             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31181         }
31182         
31183         if (tagType.ascii) {
31184             str = '';
31185             // Concatenate the chars:
31186             for (i = 0; i < values.length; i += 1) {
31187                 c = values[i];
31188                 // Ignore the terminating NULL byte(s):
31189                 if (c === '\u0000') {
31190                     break;
31191                 }
31192                 str += c;
31193             }
31194             return str;
31195         }
31196         return values;
31197     }
31198     
31199 });
31200
31201 Roo.apply(Roo.bootstrap.UploadCropbox, {
31202     tags : {
31203         'Orientation': 0x0112
31204     },
31205     
31206     Orientation: {
31207             1: 0, //'top-left',
31208 //            2: 'top-right',
31209             3: 180, //'bottom-right',
31210 //            4: 'bottom-left',
31211 //            5: 'left-top',
31212             6: 90, //'right-top',
31213 //            7: 'right-bottom',
31214             8: 270 //'left-bottom'
31215     },
31216     
31217     exifTagTypes : {
31218         // byte, 8-bit unsigned int:
31219         1: {
31220             getValue: function (dataView, dataOffset) {
31221                 return dataView.getUint8(dataOffset);
31222             },
31223             size: 1
31224         },
31225         // ascii, 8-bit byte:
31226         2: {
31227             getValue: function (dataView, dataOffset) {
31228                 return String.fromCharCode(dataView.getUint8(dataOffset));
31229             },
31230             size: 1,
31231             ascii: true
31232         },
31233         // short, 16 bit int:
31234         3: {
31235             getValue: function (dataView, dataOffset, littleEndian) {
31236                 return dataView.getUint16(dataOffset, littleEndian);
31237             },
31238             size: 2
31239         },
31240         // long, 32 bit int:
31241         4: {
31242             getValue: function (dataView, dataOffset, littleEndian) {
31243                 return dataView.getUint32(dataOffset, littleEndian);
31244             },
31245             size: 4
31246         },
31247         // rational = two long values, first is numerator, second is denominator:
31248         5: {
31249             getValue: function (dataView, dataOffset, littleEndian) {
31250                 return dataView.getUint32(dataOffset, littleEndian) /
31251                     dataView.getUint32(dataOffset + 4, littleEndian);
31252             },
31253             size: 8
31254         },
31255         // slong, 32 bit signed int:
31256         9: {
31257             getValue: function (dataView, dataOffset, littleEndian) {
31258                 return dataView.getInt32(dataOffset, littleEndian);
31259             },
31260             size: 4
31261         },
31262         // srational, two slongs, first is numerator, second is denominator:
31263         10: {
31264             getValue: function (dataView, dataOffset, littleEndian) {
31265                 return dataView.getInt32(dataOffset, littleEndian) /
31266                     dataView.getInt32(dataOffset + 4, littleEndian);
31267             },
31268             size: 8
31269         }
31270     },
31271     
31272     footer : {
31273         STANDARD : [
31274             {
31275                 tag : 'div',
31276                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31277                 action : 'rotate-left',
31278                 cn : [
31279                     {
31280                         tag : 'button',
31281                         cls : 'btn btn-default',
31282                         html : '<i class="fa fa-undo"></i>'
31283                     }
31284                 ]
31285             },
31286             {
31287                 tag : 'div',
31288                 cls : 'btn-group roo-upload-cropbox-picture',
31289                 action : 'picture',
31290                 cn : [
31291                     {
31292                         tag : 'button',
31293                         cls : 'btn btn-default',
31294                         html : '<i class="fa fa-picture-o"></i>'
31295                     }
31296                 ]
31297             },
31298             {
31299                 tag : 'div',
31300                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31301                 action : 'rotate-right',
31302                 cn : [
31303                     {
31304                         tag : 'button',
31305                         cls : 'btn btn-default',
31306                         html : '<i class="fa fa-repeat"></i>'
31307                     }
31308                 ]
31309             }
31310         ],
31311         DOCUMENT : [
31312             {
31313                 tag : 'div',
31314                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31315                 action : 'rotate-left',
31316                 cn : [
31317                     {
31318                         tag : 'button',
31319                         cls : 'btn btn-default',
31320                         html : '<i class="fa fa-undo"></i>'
31321                     }
31322                 ]
31323             },
31324             {
31325                 tag : 'div',
31326                 cls : 'btn-group roo-upload-cropbox-download',
31327                 action : 'download',
31328                 cn : [
31329                     {
31330                         tag : 'button',
31331                         cls : 'btn btn-default',
31332                         html : '<i class="fa fa-download"></i>'
31333                     }
31334                 ]
31335             },
31336             {
31337                 tag : 'div',
31338                 cls : 'btn-group roo-upload-cropbox-crop',
31339                 action : 'crop',
31340                 cn : [
31341                     {
31342                         tag : 'button',
31343                         cls : 'btn btn-default',
31344                         html : '<i class="fa fa-crop"></i>'
31345                     }
31346                 ]
31347             },
31348             {
31349                 tag : 'div',
31350                 cls : 'btn-group roo-upload-cropbox-trash',
31351                 action : 'trash',
31352                 cn : [
31353                     {
31354                         tag : 'button',
31355                         cls : 'btn btn-default',
31356                         html : '<i class="fa fa-trash"></i>'
31357                     }
31358                 ]
31359             },
31360             {
31361                 tag : 'div',
31362                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31363                 action : 'rotate-right',
31364                 cn : [
31365                     {
31366                         tag : 'button',
31367                         cls : 'btn btn-default',
31368                         html : '<i class="fa fa-repeat"></i>'
31369                     }
31370                 ]
31371             }
31372         ],
31373         ROTATOR : [
31374             {
31375                 tag : 'div',
31376                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31377                 action : 'rotate-left',
31378                 cn : [
31379                     {
31380                         tag : 'button',
31381                         cls : 'btn btn-default',
31382                         html : '<i class="fa fa-undo"></i>'
31383                     }
31384                 ]
31385             },
31386             {
31387                 tag : 'div',
31388                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31389                 action : 'rotate-right',
31390                 cn : [
31391                     {
31392                         tag : 'button',
31393                         cls : 'btn btn-default',
31394                         html : '<i class="fa fa-repeat"></i>'
31395                     }
31396                 ]
31397             }
31398         ]
31399     }
31400 });
31401
31402 /*
31403 * Licence: LGPL
31404 */
31405
31406 /**
31407  * @class Roo.bootstrap.DocumentManager
31408  * @extends Roo.bootstrap.Component
31409  * Bootstrap DocumentManager class
31410  * @cfg {String} paramName default 'imageUpload'
31411  * @cfg {String} toolTipName default 'filename'
31412  * @cfg {String} method default POST
31413  * @cfg {String} url action url
31414  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31415  * @cfg {Boolean} multiple multiple upload default true
31416  * @cfg {Number} thumbSize default 300
31417  * @cfg {String} fieldLabel
31418  * @cfg {Number} labelWidth default 4
31419  * @cfg {String} labelAlign (left|top) default left
31420  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31421 * @cfg {Number} labellg set the width of label (1-12)
31422  * @cfg {Number} labelmd set the width of label (1-12)
31423  * @cfg {Number} labelsm set the width of label (1-12)
31424  * @cfg {Number} labelxs set the width of label (1-12)
31425  * 
31426  * @constructor
31427  * Create a new DocumentManager
31428  * @param {Object} config The config object
31429  */
31430
31431 Roo.bootstrap.DocumentManager = function(config){
31432     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31433     
31434     this.files = [];
31435     this.delegates = [];
31436     
31437     this.addEvents({
31438         /**
31439          * @event initial
31440          * Fire when initial the DocumentManager
31441          * @param {Roo.bootstrap.DocumentManager} this
31442          */
31443         "initial" : true,
31444         /**
31445          * @event inspect
31446          * inspect selected file
31447          * @param {Roo.bootstrap.DocumentManager} this
31448          * @param {File} file
31449          */
31450         "inspect" : true,
31451         /**
31452          * @event exception
31453          * Fire when xhr load exception
31454          * @param {Roo.bootstrap.DocumentManager} this
31455          * @param {XMLHttpRequest} xhr
31456          */
31457         "exception" : true,
31458         /**
31459          * @event afterupload
31460          * Fire when xhr load exception
31461          * @param {Roo.bootstrap.DocumentManager} this
31462          * @param {XMLHttpRequest} xhr
31463          */
31464         "afterupload" : true,
31465         /**
31466          * @event prepare
31467          * prepare the form data
31468          * @param {Roo.bootstrap.DocumentManager} this
31469          * @param {Object} formData
31470          */
31471         "prepare" : true,
31472         /**
31473          * @event remove
31474          * Fire when remove the file
31475          * @param {Roo.bootstrap.DocumentManager} this
31476          * @param {Object} file
31477          */
31478         "remove" : true,
31479         /**
31480          * @event refresh
31481          * Fire after refresh the file
31482          * @param {Roo.bootstrap.DocumentManager} this
31483          */
31484         "refresh" : true,
31485         /**
31486          * @event click
31487          * Fire after click the image
31488          * @param {Roo.bootstrap.DocumentManager} this
31489          * @param {Object} file
31490          */
31491         "click" : true,
31492         /**
31493          * @event edit
31494          * Fire when upload a image and editable set to true
31495          * @param {Roo.bootstrap.DocumentManager} this
31496          * @param {Object} file
31497          */
31498         "edit" : true,
31499         /**
31500          * @event beforeselectfile
31501          * Fire before select file
31502          * @param {Roo.bootstrap.DocumentManager} this
31503          */
31504         "beforeselectfile" : true,
31505         /**
31506          * @event process
31507          * Fire before process file
31508          * @param {Roo.bootstrap.DocumentManager} this
31509          * @param {Object} file
31510          */
31511         "process" : true,
31512         /**
31513          * @event previewrendered
31514          * Fire when preview rendered
31515          * @param {Roo.bootstrap.DocumentManager} this
31516          * @param {Object} file
31517          */
31518         "previewrendered" : true,
31519         /**
31520          */
31521         "previewResize" : true
31522         
31523     });
31524 };
31525
31526 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31527     
31528     boxes : 0,
31529     inputName : '',
31530     thumbSize : 300,
31531     multiple : true,
31532     files : false,
31533     method : 'POST',
31534     url : '',
31535     paramName : 'imageUpload',
31536     toolTipName : 'filename',
31537     fieldLabel : '',
31538     labelWidth : 4,
31539     labelAlign : 'left',
31540     editable : true,
31541     delegates : false,
31542     xhr : false, 
31543     
31544     labellg : 0,
31545     labelmd : 0,
31546     labelsm : 0,
31547     labelxs : 0,
31548     
31549     getAutoCreate : function()
31550     {   
31551         var managerWidget = {
31552             tag : 'div',
31553             cls : 'roo-document-manager',
31554             cn : [
31555                 {
31556                     tag : 'input',
31557                     cls : 'roo-document-manager-selector',
31558                     type : 'file'
31559                 },
31560                 {
31561                     tag : 'div',
31562                     cls : 'roo-document-manager-uploader',
31563                     cn : [
31564                         {
31565                             tag : 'div',
31566                             cls : 'roo-document-manager-upload-btn',
31567                             html : '<i class="fa fa-plus"></i>'
31568                         }
31569                     ]
31570                     
31571                 }
31572             ]
31573         };
31574         
31575         var content = [
31576             {
31577                 tag : 'div',
31578                 cls : 'column col-md-12',
31579                 cn : managerWidget
31580             }
31581         ];
31582         
31583         if(this.fieldLabel.length){
31584             
31585             content = [
31586                 {
31587                     tag : 'div',
31588                     cls : 'column col-md-12',
31589                     html : this.fieldLabel
31590                 },
31591                 {
31592                     tag : 'div',
31593                     cls : 'column col-md-12',
31594                     cn : managerWidget
31595                 }
31596             ];
31597
31598             if(this.labelAlign == 'left'){
31599                 content = [
31600                     {
31601                         tag : 'div',
31602                         cls : 'column',
31603                         html : this.fieldLabel
31604                     },
31605                     {
31606                         tag : 'div',
31607                         cls : 'column',
31608                         cn : managerWidget
31609                     }
31610                 ];
31611                 
31612                 if(this.labelWidth > 12){
31613                     content[0].style = "width: " + this.labelWidth + 'px';
31614                 }
31615
31616                 if(this.labelWidth < 13 && this.labelmd == 0){
31617                     this.labelmd = this.labelWidth;
31618                 }
31619
31620                 if(this.labellg > 0){
31621                     content[0].cls += ' col-lg-' + this.labellg;
31622                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31623                 }
31624
31625                 if(this.labelmd > 0){
31626                     content[0].cls += ' col-md-' + this.labelmd;
31627                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31628                 }
31629
31630                 if(this.labelsm > 0){
31631                     content[0].cls += ' col-sm-' + this.labelsm;
31632                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31633                 }
31634
31635                 if(this.labelxs > 0){
31636                     content[0].cls += ' col-xs-' + this.labelxs;
31637                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31638                 }
31639                 
31640             }
31641         }
31642         
31643         var cfg = {
31644             tag : 'div',
31645             cls : 'row clearfix',
31646             cn : content
31647         };
31648         
31649         return cfg;
31650         
31651     },
31652     
31653     initEvents : function()
31654     {
31655         this.managerEl = this.el.select('.roo-document-manager', true).first();
31656         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31657         
31658         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31659         this.selectorEl.hide();
31660         
31661         if(this.multiple){
31662             this.selectorEl.attr('multiple', 'multiple');
31663         }
31664         
31665         this.selectorEl.on('change', this.onFileSelected, this);
31666         
31667         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31668         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31669         
31670         this.uploader.on('click', this.onUploaderClick, this);
31671         
31672         this.renderProgressDialog();
31673         
31674         var _this = this;
31675         
31676         window.addEventListener("resize", function() { _this.refresh(); } );
31677         
31678         this.fireEvent('initial', this);
31679     },
31680     
31681     renderProgressDialog : function()
31682     {
31683         var _this = this;
31684         
31685         this.progressDialog = new Roo.bootstrap.Modal({
31686             cls : 'roo-document-manager-progress-dialog',
31687             allow_close : false,
31688             animate : false,
31689             title : '',
31690             buttons : [
31691                 {
31692                     name  :'cancel',
31693                     weight : 'danger',
31694                     html : 'Cancel'
31695                 }
31696             ], 
31697             listeners : { 
31698                 btnclick : function() {
31699                     _this.uploadCancel();
31700                     this.hide();
31701                 }
31702             }
31703         });
31704          
31705         this.progressDialog.render(Roo.get(document.body));
31706          
31707         this.progress = new Roo.bootstrap.Progress({
31708             cls : 'roo-document-manager-progress',
31709             active : true,
31710             striped : true
31711         });
31712         
31713         this.progress.render(this.progressDialog.getChildContainer());
31714         
31715         this.progressBar = new Roo.bootstrap.ProgressBar({
31716             cls : 'roo-document-manager-progress-bar',
31717             aria_valuenow : 0,
31718             aria_valuemin : 0,
31719             aria_valuemax : 12,
31720             panel : 'success'
31721         });
31722         
31723         this.progressBar.render(this.progress.getChildContainer());
31724     },
31725     
31726     onUploaderClick : function(e)
31727     {
31728         e.preventDefault();
31729      
31730         if(this.fireEvent('beforeselectfile', this) != false){
31731             this.selectorEl.dom.click();
31732         }
31733         
31734     },
31735     
31736     onFileSelected : function(e)
31737     {
31738         e.preventDefault();
31739         
31740         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31741             return;
31742         }
31743         
31744         Roo.each(this.selectorEl.dom.files, function(file){
31745             if(this.fireEvent('inspect', this, file) != false){
31746                 this.files.push(file);
31747             }
31748         }, this);
31749         
31750         this.queue();
31751         
31752     },
31753     
31754     queue : function()
31755     {
31756         this.selectorEl.dom.value = '';
31757         
31758         if(!this.files || !this.files.length){
31759             return;
31760         }
31761         
31762         if(this.boxes > 0 && this.files.length > this.boxes){
31763             this.files = this.files.slice(0, this.boxes);
31764         }
31765         
31766         this.uploader.show();
31767         
31768         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31769             this.uploader.hide();
31770         }
31771         
31772         var _this = this;
31773         
31774         var files = [];
31775         
31776         var docs = [];
31777         
31778         Roo.each(this.files, function(file){
31779             
31780             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31781                 var f = this.renderPreview(file);
31782                 files.push(f);
31783                 return;
31784             }
31785             
31786             if(file.type.indexOf('image') != -1){
31787                 this.delegates.push(
31788                     (function(){
31789                         _this.process(file);
31790                     }).createDelegate(this)
31791                 );
31792         
31793                 return;
31794             }
31795             
31796             docs.push(
31797                 (function(){
31798                     _this.process(file);
31799                 }).createDelegate(this)
31800             );
31801             
31802         }, this);
31803         
31804         this.files = files;
31805         
31806         this.delegates = this.delegates.concat(docs);
31807         
31808         if(!this.delegates.length){
31809             this.refresh();
31810             return;
31811         }
31812         
31813         this.progressBar.aria_valuemax = this.delegates.length;
31814         
31815         this.arrange();
31816         
31817         return;
31818     },
31819     
31820     arrange : function()
31821     {
31822         if(!this.delegates.length){
31823             this.progressDialog.hide();
31824             this.refresh();
31825             return;
31826         }
31827         
31828         var delegate = this.delegates.shift();
31829         
31830         this.progressDialog.show();
31831         
31832         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31833         
31834         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31835         
31836         delegate();
31837     },
31838     
31839     refresh : function()
31840     {
31841         this.uploader.show();
31842         
31843         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31844             this.uploader.hide();
31845         }
31846         
31847         Roo.isTouch ? this.closable(false) : this.closable(true);
31848         
31849         this.fireEvent('refresh', this);
31850     },
31851     
31852     onRemove : function(e, el, o)
31853     {
31854         e.preventDefault();
31855         
31856         this.fireEvent('remove', this, o);
31857         
31858     },
31859     
31860     remove : function(o)
31861     {
31862         var files = [];
31863         
31864         Roo.each(this.files, function(file){
31865             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31866                 files.push(file);
31867                 return;
31868             }
31869
31870             o.target.remove();
31871
31872         }, this);
31873         
31874         this.files = files;
31875         
31876         this.refresh();
31877     },
31878     
31879     clear : function()
31880     {
31881         Roo.each(this.files, function(file){
31882             if(!file.target){
31883                 return;
31884             }
31885             
31886             file.target.remove();
31887
31888         }, this);
31889         
31890         this.files = [];
31891         
31892         this.refresh();
31893     },
31894     
31895     onClick : function(e, el, o)
31896     {
31897         e.preventDefault();
31898         
31899         this.fireEvent('click', this, o);
31900         
31901     },
31902     
31903     closable : function(closable)
31904     {
31905         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31906             
31907             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31908             
31909             if(closable){
31910                 el.show();
31911                 return;
31912             }
31913             
31914             el.hide();
31915             
31916         }, this);
31917     },
31918     
31919     xhrOnLoad : function(xhr)
31920     {
31921         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31922             el.remove();
31923         }, this);
31924         
31925         if (xhr.readyState !== 4) {
31926             this.arrange();
31927             this.fireEvent('exception', this, xhr);
31928             return;
31929         }
31930
31931         var response = Roo.decode(xhr.responseText);
31932         
31933         if(!response.success){
31934             this.arrange();
31935             this.fireEvent('exception', this, xhr);
31936             return;
31937         }
31938         
31939         var file = this.renderPreview(response.data);
31940         
31941         this.files.push(file);
31942         
31943         this.arrange();
31944         
31945         this.fireEvent('afterupload', this, xhr);
31946         
31947     },
31948     
31949     xhrOnError : function(xhr)
31950     {
31951         Roo.log('xhr on error');
31952         
31953         var response = Roo.decode(xhr.responseText);
31954           
31955         Roo.log(response);
31956         
31957         this.arrange();
31958     },
31959     
31960     process : function(file)
31961     {
31962         if(this.fireEvent('process', this, file) !== false){
31963             if(this.editable && file.type.indexOf('image') != -1){
31964                 this.fireEvent('edit', this, file);
31965                 return;
31966             }
31967
31968             this.uploadStart(file, false);
31969
31970             return;
31971         }
31972         
31973     },
31974     
31975     uploadStart : function(file, crop)
31976     {
31977         this.xhr = new XMLHttpRequest();
31978         
31979         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31980             this.arrange();
31981             return;
31982         }
31983         
31984         file.xhr = this.xhr;
31985             
31986         this.managerEl.createChild({
31987             tag : 'div',
31988             cls : 'roo-document-manager-loading',
31989             cn : [
31990                 {
31991                     tag : 'div',
31992                     tooltip : file.name,
31993                     cls : 'roo-document-manager-thumb',
31994                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31995                 }
31996             ]
31997
31998         });
31999
32000         this.xhr.open(this.method, this.url, true);
32001         
32002         var headers = {
32003             "Accept": "application/json",
32004             "Cache-Control": "no-cache",
32005             "X-Requested-With": "XMLHttpRequest"
32006         };
32007         
32008         for (var headerName in headers) {
32009             var headerValue = headers[headerName];
32010             if (headerValue) {
32011                 this.xhr.setRequestHeader(headerName, headerValue);
32012             }
32013         }
32014         
32015         var _this = this;
32016         
32017         this.xhr.onload = function()
32018         {
32019             _this.xhrOnLoad(_this.xhr);
32020         }
32021         
32022         this.xhr.onerror = function()
32023         {
32024             _this.xhrOnError(_this.xhr);
32025         }
32026         
32027         var formData = new FormData();
32028
32029         formData.append('returnHTML', 'NO');
32030         
32031         if(crop){
32032             formData.append('crop', crop);
32033         }
32034         
32035         formData.append(this.paramName, file, file.name);
32036         
32037         var options = {
32038             file : file, 
32039             manually : false
32040         };
32041         
32042         if(this.fireEvent('prepare', this, formData, options) != false){
32043             
32044             if(options.manually){
32045                 return;
32046             }
32047             
32048             this.xhr.send(formData);
32049             return;
32050         };
32051         
32052         this.uploadCancel();
32053     },
32054     
32055     uploadCancel : function()
32056     {
32057         if (this.xhr) {
32058             this.xhr.abort();
32059         }
32060         
32061         this.delegates = [];
32062         
32063         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32064             el.remove();
32065         }, this);
32066         
32067         this.arrange();
32068     },
32069     
32070     renderPreview : function(file)
32071     {
32072         if(typeof(file.target) != 'undefined' && file.target){
32073             return file;
32074         }
32075         
32076         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32077         
32078         var previewEl = this.managerEl.createChild({
32079             tag : 'div',
32080             cls : 'roo-document-manager-preview',
32081             cn : [
32082                 {
32083                     tag : 'div',
32084                     tooltip : file[this.toolTipName],
32085                     cls : 'roo-document-manager-thumb',
32086                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32087                 },
32088                 {
32089                     tag : 'button',
32090                     cls : 'close',
32091                     html : '<i class="fa fa-times-circle"></i>'
32092                 }
32093             ]
32094         });
32095
32096         var close = previewEl.select('button.close', true).first();
32097
32098         close.on('click', this.onRemove, this, file);
32099
32100         file.target = previewEl;
32101
32102         var image = previewEl.select('img', true).first();
32103         
32104         var _this = this;
32105         
32106         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32107         
32108         image.on('click', this.onClick, this, file);
32109         
32110         this.fireEvent('previewrendered', this, file);
32111         
32112         return file;
32113         
32114     },
32115     
32116     onPreviewLoad : function(file, image)
32117     {
32118         if(typeof(file.target) == 'undefined' || !file.target){
32119             return;
32120         }
32121         
32122         var width = image.dom.naturalWidth || image.dom.width;
32123         var height = image.dom.naturalHeight || image.dom.height;
32124         
32125         if(!this.previewResize) {
32126             return;
32127         }
32128         
32129         if(width > height){
32130             file.target.addClass('wide');
32131             return;
32132         }
32133         
32134         file.target.addClass('tall');
32135         return;
32136         
32137     },
32138     
32139     uploadFromSource : function(file, crop)
32140     {
32141         this.xhr = new XMLHttpRequest();
32142         
32143         this.managerEl.createChild({
32144             tag : 'div',
32145             cls : 'roo-document-manager-loading',
32146             cn : [
32147                 {
32148                     tag : 'div',
32149                     tooltip : file.name,
32150                     cls : 'roo-document-manager-thumb',
32151                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32152                 }
32153             ]
32154
32155         });
32156
32157         this.xhr.open(this.method, this.url, true);
32158         
32159         var headers = {
32160             "Accept": "application/json",
32161             "Cache-Control": "no-cache",
32162             "X-Requested-With": "XMLHttpRequest"
32163         };
32164         
32165         for (var headerName in headers) {
32166             var headerValue = headers[headerName];
32167             if (headerValue) {
32168                 this.xhr.setRequestHeader(headerName, headerValue);
32169             }
32170         }
32171         
32172         var _this = this;
32173         
32174         this.xhr.onload = function()
32175         {
32176             _this.xhrOnLoad(_this.xhr);
32177         }
32178         
32179         this.xhr.onerror = function()
32180         {
32181             _this.xhrOnError(_this.xhr);
32182         }
32183         
32184         var formData = new FormData();
32185
32186         formData.append('returnHTML', 'NO');
32187         
32188         formData.append('crop', crop);
32189         
32190         if(typeof(file.filename) != 'undefined'){
32191             formData.append('filename', file.filename);
32192         }
32193         
32194         if(typeof(file.mimetype) != 'undefined'){
32195             formData.append('mimetype', file.mimetype);
32196         }
32197         
32198         Roo.log(formData);
32199         
32200         if(this.fireEvent('prepare', this, formData) != false){
32201             this.xhr.send(formData);
32202         };
32203     }
32204 });
32205
32206 /*
32207 * Licence: LGPL
32208 */
32209
32210 /**
32211  * @class Roo.bootstrap.DocumentViewer
32212  * @extends Roo.bootstrap.Component
32213  * Bootstrap DocumentViewer class
32214  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32215  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32216  * 
32217  * @constructor
32218  * Create a new DocumentViewer
32219  * @param {Object} config The config object
32220  */
32221
32222 Roo.bootstrap.DocumentViewer = function(config){
32223     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32224     
32225     this.addEvents({
32226         /**
32227          * @event initial
32228          * Fire after initEvent
32229          * @param {Roo.bootstrap.DocumentViewer} this
32230          */
32231         "initial" : true,
32232         /**
32233          * @event click
32234          * Fire after click
32235          * @param {Roo.bootstrap.DocumentViewer} this
32236          */
32237         "click" : true,
32238         /**
32239          * @event download
32240          * Fire after download button
32241          * @param {Roo.bootstrap.DocumentViewer} this
32242          */
32243         "download" : true,
32244         /**
32245          * @event trash
32246          * Fire after trash button
32247          * @param {Roo.bootstrap.DocumentViewer} this
32248          */
32249         "trash" : true
32250         
32251     });
32252 };
32253
32254 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32255     
32256     showDownload : true,
32257     
32258     showTrash : true,
32259     
32260     getAutoCreate : function()
32261     {
32262         var cfg = {
32263             tag : 'div',
32264             cls : 'roo-document-viewer',
32265             cn : [
32266                 {
32267                     tag : 'div',
32268                     cls : 'roo-document-viewer-body',
32269                     cn : [
32270                         {
32271                             tag : 'div',
32272                             cls : 'roo-document-viewer-thumb',
32273                             cn : [
32274                                 {
32275                                     tag : 'img',
32276                                     cls : 'roo-document-viewer-image'
32277                                 }
32278                             ]
32279                         }
32280                     ]
32281                 },
32282                 {
32283                     tag : 'div',
32284                     cls : 'roo-document-viewer-footer',
32285                     cn : {
32286                         tag : 'div',
32287                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32288                         cn : [
32289                             {
32290                                 tag : 'div',
32291                                 cls : 'btn-group roo-document-viewer-download',
32292                                 cn : [
32293                                     {
32294                                         tag : 'button',
32295                                         cls : 'btn btn-default',
32296                                         html : '<i class="fa fa-download"></i>'
32297                                     }
32298                                 ]
32299                             },
32300                             {
32301                                 tag : 'div',
32302                                 cls : 'btn-group roo-document-viewer-trash',
32303                                 cn : [
32304                                     {
32305                                         tag : 'button',
32306                                         cls : 'btn btn-default',
32307                                         html : '<i class="fa fa-trash"></i>'
32308                                     }
32309                                 ]
32310                             }
32311                         ]
32312                     }
32313                 }
32314             ]
32315         };
32316         
32317         return cfg;
32318     },
32319     
32320     initEvents : function()
32321     {
32322         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32323         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32324         
32325         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32326         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32327         
32328         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32329         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32330         
32331         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32332         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32333         
32334         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32335         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32336         
32337         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32338         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32339         
32340         this.bodyEl.on('click', this.onClick, this);
32341         this.downloadBtn.on('click', this.onDownload, this);
32342         this.trashBtn.on('click', this.onTrash, this);
32343         
32344         this.downloadBtn.hide();
32345         this.trashBtn.hide();
32346         
32347         if(this.showDownload){
32348             this.downloadBtn.show();
32349         }
32350         
32351         if(this.showTrash){
32352             this.trashBtn.show();
32353         }
32354         
32355         if(!this.showDownload && !this.showTrash) {
32356             this.footerEl.hide();
32357         }
32358         
32359     },
32360     
32361     initial : function()
32362     {
32363         this.fireEvent('initial', this);
32364         
32365     },
32366     
32367     onClick : function(e)
32368     {
32369         e.preventDefault();
32370         
32371         this.fireEvent('click', this);
32372     },
32373     
32374     onDownload : function(e)
32375     {
32376         e.preventDefault();
32377         
32378         this.fireEvent('download', this);
32379     },
32380     
32381     onTrash : function(e)
32382     {
32383         e.preventDefault();
32384         
32385         this.fireEvent('trash', this);
32386     }
32387     
32388 });
32389 /*
32390  * - LGPL
32391  *
32392  * nav progress bar
32393  * 
32394  */
32395
32396 /**
32397  * @class Roo.bootstrap.NavProgressBar
32398  * @extends Roo.bootstrap.Component
32399  * Bootstrap NavProgressBar class
32400  * 
32401  * @constructor
32402  * Create a new nav progress bar
32403  * @param {Object} config The config object
32404  */
32405
32406 Roo.bootstrap.NavProgressBar = function(config){
32407     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32408
32409     this.bullets = this.bullets || [];
32410    
32411 //    Roo.bootstrap.NavProgressBar.register(this);
32412      this.addEvents({
32413         /**
32414              * @event changed
32415              * Fires when the active item changes
32416              * @param {Roo.bootstrap.NavProgressBar} this
32417              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32418              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32419          */
32420         'changed': true
32421      });
32422     
32423 };
32424
32425 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32426     
32427     bullets : [],
32428     barItems : [],
32429     
32430     getAutoCreate : function()
32431     {
32432         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32433         
32434         cfg = {
32435             tag : 'div',
32436             cls : 'roo-navigation-bar-group',
32437             cn : [
32438                 {
32439                     tag : 'div',
32440                     cls : 'roo-navigation-top-bar'
32441                 },
32442                 {
32443                     tag : 'div',
32444                     cls : 'roo-navigation-bullets-bar',
32445                     cn : [
32446                         {
32447                             tag : 'ul',
32448                             cls : 'roo-navigation-bar'
32449                         }
32450                     ]
32451                 },
32452                 
32453                 {
32454                     tag : 'div',
32455                     cls : 'roo-navigation-bottom-bar'
32456                 }
32457             ]
32458             
32459         };
32460         
32461         return cfg;
32462         
32463     },
32464     
32465     initEvents: function() 
32466     {
32467         
32468     },
32469     
32470     onRender : function(ct, position) 
32471     {
32472         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32473         
32474         if(this.bullets.length){
32475             Roo.each(this.bullets, function(b){
32476                this.addItem(b);
32477             }, this);
32478         }
32479         
32480         this.format();
32481         
32482     },
32483     
32484     addItem : function(cfg)
32485     {
32486         var item = new Roo.bootstrap.NavProgressItem(cfg);
32487         
32488         item.parentId = this.id;
32489         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32490         
32491         if(cfg.html){
32492             var top = new Roo.bootstrap.Element({
32493                 tag : 'div',
32494                 cls : 'roo-navigation-bar-text'
32495             });
32496             
32497             var bottom = new Roo.bootstrap.Element({
32498                 tag : 'div',
32499                 cls : 'roo-navigation-bar-text'
32500             });
32501             
32502             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32503             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32504             
32505             var topText = new Roo.bootstrap.Element({
32506                 tag : 'span',
32507                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32508             });
32509             
32510             var bottomText = new Roo.bootstrap.Element({
32511                 tag : 'span',
32512                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32513             });
32514             
32515             topText.onRender(top.el, null);
32516             bottomText.onRender(bottom.el, null);
32517             
32518             item.topEl = top;
32519             item.bottomEl = bottom;
32520         }
32521         
32522         this.barItems.push(item);
32523         
32524         return item;
32525     },
32526     
32527     getActive : function()
32528     {
32529         var active = false;
32530         
32531         Roo.each(this.barItems, function(v){
32532             
32533             if (!v.isActive()) {
32534                 return;
32535             }
32536             
32537             active = v;
32538             return false;
32539             
32540         });
32541         
32542         return active;
32543     },
32544     
32545     setActiveItem : function(item)
32546     {
32547         var prev = false;
32548         
32549         Roo.each(this.barItems, function(v){
32550             if (v.rid == item.rid) {
32551                 return ;
32552             }
32553             
32554             if (v.isActive()) {
32555                 v.setActive(false);
32556                 prev = v;
32557             }
32558         });
32559
32560         item.setActive(true);
32561         
32562         this.fireEvent('changed', this, item, prev);
32563     },
32564     
32565     getBarItem: function(rid)
32566     {
32567         var ret = false;
32568         
32569         Roo.each(this.barItems, function(e) {
32570             if (e.rid != rid) {
32571                 return;
32572             }
32573             
32574             ret =  e;
32575             return false;
32576         });
32577         
32578         return ret;
32579     },
32580     
32581     indexOfItem : function(item)
32582     {
32583         var index = false;
32584         
32585         Roo.each(this.barItems, function(v, i){
32586             
32587             if (v.rid != item.rid) {
32588                 return;
32589             }
32590             
32591             index = i;
32592             return false
32593         });
32594         
32595         return index;
32596     },
32597     
32598     setActiveNext : function()
32599     {
32600         var i = this.indexOfItem(this.getActive());
32601         
32602         if (i > this.barItems.length) {
32603             return;
32604         }
32605         
32606         this.setActiveItem(this.barItems[i+1]);
32607     },
32608     
32609     setActivePrev : function()
32610     {
32611         var i = this.indexOfItem(this.getActive());
32612         
32613         if (i  < 1) {
32614             return;
32615         }
32616         
32617         this.setActiveItem(this.barItems[i-1]);
32618     },
32619     
32620     format : function()
32621     {
32622         if(!this.barItems.length){
32623             return;
32624         }
32625      
32626         var width = 100 / this.barItems.length;
32627         
32628         Roo.each(this.barItems, function(i){
32629             i.el.setStyle('width', width + '%');
32630             i.topEl.el.setStyle('width', width + '%');
32631             i.bottomEl.el.setStyle('width', width + '%');
32632         }, this);
32633         
32634     }
32635     
32636 });
32637 /*
32638  * - LGPL
32639  *
32640  * Nav Progress Item
32641  * 
32642  */
32643
32644 /**
32645  * @class Roo.bootstrap.NavProgressItem
32646  * @extends Roo.bootstrap.Component
32647  * Bootstrap NavProgressItem class
32648  * @cfg {String} rid the reference id
32649  * @cfg {Boolean} active (true|false) Is item active default false
32650  * @cfg {Boolean} disabled (true|false) Is item active default false
32651  * @cfg {String} html
32652  * @cfg {String} position (top|bottom) text position default bottom
32653  * @cfg {String} icon show icon instead of number
32654  * 
32655  * @constructor
32656  * Create a new NavProgressItem
32657  * @param {Object} config The config object
32658  */
32659 Roo.bootstrap.NavProgressItem = function(config){
32660     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32661     this.addEvents({
32662         // raw events
32663         /**
32664          * @event click
32665          * The raw click event for the entire grid.
32666          * @param {Roo.bootstrap.NavProgressItem} this
32667          * @param {Roo.EventObject} e
32668          */
32669         "click" : true
32670     });
32671    
32672 };
32673
32674 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32675     
32676     rid : '',
32677     active : false,
32678     disabled : false,
32679     html : '',
32680     position : 'bottom',
32681     icon : false,
32682     
32683     getAutoCreate : function()
32684     {
32685         var iconCls = 'roo-navigation-bar-item-icon';
32686         
32687         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32688         
32689         var cfg = {
32690             tag: 'li',
32691             cls: 'roo-navigation-bar-item',
32692             cn : [
32693                 {
32694                     tag : 'i',
32695                     cls : iconCls
32696                 }
32697             ]
32698         };
32699         
32700         if(this.active){
32701             cfg.cls += ' active';
32702         }
32703         if(this.disabled){
32704             cfg.cls += ' disabled';
32705         }
32706         
32707         return cfg;
32708     },
32709     
32710     disable : function()
32711     {
32712         this.setDisabled(true);
32713     },
32714     
32715     enable : function()
32716     {
32717         this.setDisabled(false);
32718     },
32719     
32720     initEvents: function() 
32721     {
32722         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32723         
32724         this.iconEl.on('click', this.onClick, this);
32725     },
32726     
32727     onClick : function(e)
32728     {
32729         e.preventDefault();
32730         
32731         if(this.disabled){
32732             return;
32733         }
32734         
32735         if(this.fireEvent('click', this, e) === false){
32736             return;
32737         };
32738         
32739         this.parent().setActiveItem(this);
32740     },
32741     
32742     isActive: function () 
32743     {
32744         return this.active;
32745     },
32746     
32747     setActive : function(state)
32748     {
32749         if(this.active == state){
32750             return;
32751         }
32752         
32753         this.active = state;
32754         
32755         if (state) {
32756             this.el.addClass('active');
32757             return;
32758         }
32759         
32760         this.el.removeClass('active');
32761         
32762         return;
32763     },
32764     
32765     setDisabled : function(state)
32766     {
32767         if(this.disabled == state){
32768             return;
32769         }
32770         
32771         this.disabled = state;
32772         
32773         if (state) {
32774             this.el.addClass('disabled');
32775             return;
32776         }
32777         
32778         this.el.removeClass('disabled');
32779     },
32780     
32781     tooltipEl : function()
32782     {
32783         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32784     }
32785 });
32786  
32787
32788  /*
32789  * - LGPL
32790  *
32791  * FieldLabel
32792  * 
32793  */
32794
32795 /**
32796  * @class Roo.bootstrap.FieldLabel
32797  * @extends Roo.bootstrap.Component
32798  * Bootstrap FieldLabel class
32799  * @cfg {String} html contents of the element
32800  * @cfg {String} tag tag of the element default label
32801  * @cfg {String} cls class of the element
32802  * @cfg {String} target label target 
32803  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32804  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32805  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32806  * @cfg {String} iconTooltip default "This field is required"
32807  * @cfg {String} indicatorpos (left|right) default left
32808  * 
32809  * @constructor
32810  * Create a new FieldLabel
32811  * @param {Object} config The config object
32812  */
32813
32814 Roo.bootstrap.FieldLabel = function(config){
32815     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32816     
32817     this.addEvents({
32818             /**
32819              * @event invalid
32820              * Fires after the field has been marked as invalid.
32821              * @param {Roo.form.FieldLabel} this
32822              * @param {String} msg The validation message
32823              */
32824             invalid : true,
32825             /**
32826              * @event valid
32827              * Fires after the field has been validated with no errors.
32828              * @param {Roo.form.FieldLabel} this
32829              */
32830             valid : true
32831         });
32832 };
32833
32834 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32835     
32836     tag: 'label',
32837     cls: '',
32838     html: '',
32839     target: '',
32840     allowBlank : true,
32841     invalidClass : 'has-warning',
32842     validClass : 'has-success',
32843     iconTooltip : 'This field is required',
32844     indicatorpos : 'left',
32845     
32846     getAutoCreate : function(){
32847         
32848         var cls = "";
32849         if (!this.allowBlank) {
32850             cls  = "visible";
32851         }
32852         
32853         var cfg = {
32854             tag : this.tag,
32855             cls : 'roo-bootstrap-field-label ' + this.cls,
32856             for : this.target,
32857             cn : [
32858                 {
32859                     tag : 'i',
32860                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32861                     tooltip : this.iconTooltip
32862                 },
32863                 {
32864                     tag : 'span',
32865                     html : this.html
32866                 }
32867             ] 
32868         };
32869         
32870         if(this.indicatorpos == 'right'){
32871             var cfg = {
32872                 tag : this.tag,
32873                 cls : 'roo-bootstrap-field-label ' + this.cls,
32874                 for : this.target,
32875                 cn : [
32876                     {
32877                         tag : 'span',
32878                         html : this.html
32879                     },
32880                     {
32881                         tag : 'i',
32882                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32883                         tooltip : this.iconTooltip
32884                     }
32885                 ] 
32886             };
32887         }
32888         
32889         return cfg;
32890     },
32891     
32892     initEvents: function() 
32893     {
32894         Roo.bootstrap.Element.superclass.initEvents.call(this);
32895         
32896         this.indicator = this.indicatorEl();
32897         
32898         if(this.indicator){
32899             this.indicator.removeClass('visible');
32900             this.indicator.addClass('invisible');
32901         }
32902         
32903         Roo.bootstrap.FieldLabel.register(this);
32904     },
32905     
32906     indicatorEl : function()
32907     {
32908         var indicator = this.el.select('i.roo-required-indicator',true).first();
32909         
32910         if(!indicator){
32911             return false;
32912         }
32913         
32914         return indicator;
32915         
32916     },
32917     
32918     /**
32919      * Mark this field as valid
32920      */
32921     markValid : function()
32922     {
32923         if(this.indicator){
32924             this.indicator.removeClass('visible');
32925             this.indicator.addClass('invisible');
32926         }
32927         if (Roo.bootstrap.version == 3) {
32928             this.el.removeClass(this.invalidClass);
32929             this.el.addClass(this.validClass);
32930         } else {
32931             this.el.removeClass('is-invalid');
32932             this.el.addClass('is-valid');
32933         }
32934         
32935         
32936         this.fireEvent('valid', this);
32937     },
32938     
32939     /**
32940      * Mark this field as invalid
32941      * @param {String} msg The validation message
32942      */
32943     markInvalid : function(msg)
32944     {
32945         if(this.indicator){
32946             this.indicator.removeClass('invisible');
32947             this.indicator.addClass('visible');
32948         }
32949           if (Roo.bootstrap.version == 3) {
32950             this.el.removeClass(this.validClass);
32951             this.el.addClass(this.invalidClass);
32952         } else {
32953             this.el.removeClass('is-valid');
32954             this.el.addClass('is-invalid');
32955         }
32956         
32957         
32958         this.fireEvent('invalid', this, msg);
32959     }
32960     
32961    
32962 });
32963
32964 Roo.apply(Roo.bootstrap.FieldLabel, {
32965     
32966     groups: {},
32967     
32968      /**
32969     * register a FieldLabel Group
32970     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32971     */
32972     register : function(label)
32973     {
32974         if(this.groups.hasOwnProperty(label.target)){
32975             return;
32976         }
32977      
32978         this.groups[label.target] = label;
32979         
32980     },
32981     /**
32982     * fetch a FieldLabel Group based on the target
32983     * @param {string} target
32984     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32985     */
32986     get: function(target) {
32987         if (typeof(this.groups[target]) == 'undefined') {
32988             return false;
32989         }
32990         
32991         return this.groups[target] ;
32992     }
32993 });
32994
32995  
32996
32997  /*
32998  * - LGPL
32999  *
33000  * page DateSplitField.
33001  * 
33002  */
33003
33004
33005 /**
33006  * @class Roo.bootstrap.DateSplitField
33007  * @extends Roo.bootstrap.Component
33008  * Bootstrap DateSplitField class
33009  * @cfg {string} fieldLabel - the label associated
33010  * @cfg {Number} labelWidth set the width of label (0-12)
33011  * @cfg {String} labelAlign (top|left)
33012  * @cfg {Boolean} dayAllowBlank (true|false) default false
33013  * @cfg {Boolean} monthAllowBlank (true|false) default false
33014  * @cfg {Boolean} yearAllowBlank (true|false) default false
33015  * @cfg {string} dayPlaceholder 
33016  * @cfg {string} monthPlaceholder
33017  * @cfg {string} yearPlaceholder
33018  * @cfg {string} dayFormat default 'd'
33019  * @cfg {string} monthFormat default 'm'
33020  * @cfg {string} yearFormat default 'Y'
33021  * @cfg {Number} labellg set the width of label (1-12)
33022  * @cfg {Number} labelmd set the width of label (1-12)
33023  * @cfg {Number} labelsm set the width of label (1-12)
33024  * @cfg {Number} labelxs set the width of label (1-12)
33025
33026  *     
33027  * @constructor
33028  * Create a new DateSplitField
33029  * @param {Object} config The config object
33030  */
33031
33032 Roo.bootstrap.DateSplitField = function(config){
33033     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33034     
33035     this.addEvents({
33036         // raw events
33037          /**
33038          * @event years
33039          * getting the data of years
33040          * @param {Roo.bootstrap.DateSplitField} this
33041          * @param {Object} years
33042          */
33043         "years" : true,
33044         /**
33045          * @event days
33046          * getting the data of days
33047          * @param {Roo.bootstrap.DateSplitField} this
33048          * @param {Object} days
33049          */
33050         "days" : true,
33051         /**
33052          * @event invalid
33053          * Fires after the field has been marked as invalid.
33054          * @param {Roo.form.Field} this
33055          * @param {String} msg The validation message
33056          */
33057         invalid : true,
33058        /**
33059          * @event valid
33060          * Fires after the field has been validated with no errors.
33061          * @param {Roo.form.Field} this
33062          */
33063         valid : true
33064     });
33065 };
33066
33067 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33068     
33069     fieldLabel : '',
33070     labelAlign : 'top',
33071     labelWidth : 3,
33072     dayAllowBlank : false,
33073     monthAllowBlank : false,
33074     yearAllowBlank : false,
33075     dayPlaceholder : '',
33076     monthPlaceholder : '',
33077     yearPlaceholder : '',
33078     dayFormat : 'd',
33079     monthFormat : 'm',
33080     yearFormat : 'Y',
33081     isFormField : true,
33082     labellg : 0,
33083     labelmd : 0,
33084     labelsm : 0,
33085     labelxs : 0,
33086     
33087     getAutoCreate : function()
33088     {
33089         var cfg = {
33090             tag : 'div',
33091             cls : 'row roo-date-split-field-group',
33092             cn : [
33093                 {
33094                     tag : 'input',
33095                     type : 'hidden',
33096                     cls : 'form-hidden-field roo-date-split-field-group-value',
33097                     name : this.name
33098                 }
33099             ]
33100         };
33101         
33102         var labelCls = 'col-md-12';
33103         var contentCls = 'col-md-4';
33104         
33105         if(this.fieldLabel){
33106             
33107             var label = {
33108                 tag : 'div',
33109                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33110                 cn : [
33111                     {
33112                         tag : 'label',
33113                         html : this.fieldLabel
33114                     }
33115                 ]
33116             };
33117             
33118             if(this.labelAlign == 'left'){
33119             
33120                 if(this.labelWidth > 12){
33121                     label.style = "width: " + this.labelWidth + 'px';
33122                 }
33123
33124                 if(this.labelWidth < 13 && this.labelmd == 0){
33125                     this.labelmd = this.labelWidth;
33126                 }
33127
33128                 if(this.labellg > 0){
33129                     labelCls = ' col-lg-' + this.labellg;
33130                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33131                 }
33132
33133                 if(this.labelmd > 0){
33134                     labelCls = ' col-md-' + this.labelmd;
33135                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33136                 }
33137
33138                 if(this.labelsm > 0){
33139                     labelCls = ' col-sm-' + this.labelsm;
33140                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33141                 }
33142
33143                 if(this.labelxs > 0){
33144                     labelCls = ' col-xs-' + this.labelxs;
33145                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33146                 }
33147             }
33148             
33149             label.cls += ' ' + labelCls;
33150             
33151             cfg.cn.push(label);
33152         }
33153         
33154         Roo.each(['day', 'month', 'year'], function(t){
33155             cfg.cn.push({
33156                 tag : 'div',
33157                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33158             });
33159         }, this);
33160         
33161         return cfg;
33162     },
33163     
33164     inputEl: function ()
33165     {
33166         return this.el.select('.roo-date-split-field-group-value', true).first();
33167     },
33168     
33169     onRender : function(ct, position) 
33170     {
33171         var _this = this;
33172         
33173         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33174         
33175         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33176         
33177         this.dayField = new Roo.bootstrap.ComboBox({
33178             allowBlank : this.dayAllowBlank,
33179             alwaysQuery : true,
33180             displayField : 'value',
33181             editable : false,
33182             fieldLabel : '',
33183             forceSelection : true,
33184             mode : 'local',
33185             placeholder : this.dayPlaceholder,
33186             selectOnFocus : true,
33187             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33188             triggerAction : 'all',
33189             typeAhead : true,
33190             valueField : 'value',
33191             store : new Roo.data.SimpleStore({
33192                 data : (function() {    
33193                     var days = [];
33194                     _this.fireEvent('days', _this, days);
33195                     return days;
33196                 })(),
33197                 fields : [ 'value' ]
33198             }),
33199             listeners : {
33200                 select : function (_self, record, index)
33201                 {
33202                     _this.setValue(_this.getValue());
33203                 }
33204             }
33205         });
33206
33207         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33208         
33209         this.monthField = new Roo.bootstrap.MonthField({
33210             after : '<i class=\"fa fa-calendar\"></i>',
33211             allowBlank : this.monthAllowBlank,
33212             placeholder : this.monthPlaceholder,
33213             readOnly : true,
33214             listeners : {
33215                 render : function (_self)
33216                 {
33217                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33218                         e.preventDefault();
33219                         _self.focus();
33220                     });
33221                 },
33222                 select : function (_self, oldvalue, newvalue)
33223                 {
33224                     _this.setValue(_this.getValue());
33225                 }
33226             }
33227         });
33228         
33229         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33230         
33231         this.yearField = new Roo.bootstrap.ComboBox({
33232             allowBlank : this.yearAllowBlank,
33233             alwaysQuery : true,
33234             displayField : 'value',
33235             editable : false,
33236             fieldLabel : '',
33237             forceSelection : true,
33238             mode : 'local',
33239             placeholder : this.yearPlaceholder,
33240             selectOnFocus : true,
33241             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33242             triggerAction : 'all',
33243             typeAhead : true,
33244             valueField : 'value',
33245             store : new Roo.data.SimpleStore({
33246                 data : (function() {
33247                     var years = [];
33248                     _this.fireEvent('years', _this, years);
33249                     return years;
33250                 })(),
33251                 fields : [ 'value' ]
33252             }),
33253             listeners : {
33254                 select : function (_self, record, index)
33255                 {
33256                     _this.setValue(_this.getValue());
33257                 }
33258             }
33259         });
33260
33261         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33262     },
33263     
33264     setValue : function(v, format)
33265     {
33266         this.inputEl.dom.value = v;
33267         
33268         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33269         
33270         var d = Date.parseDate(v, f);
33271         
33272         if(!d){
33273             this.validate();
33274             return;
33275         }
33276         
33277         this.setDay(d.format(this.dayFormat));
33278         this.setMonth(d.format(this.monthFormat));
33279         this.setYear(d.format(this.yearFormat));
33280         
33281         this.validate();
33282         
33283         return;
33284     },
33285     
33286     setDay : function(v)
33287     {
33288         this.dayField.setValue(v);
33289         this.inputEl.dom.value = this.getValue();
33290         this.validate();
33291         return;
33292     },
33293     
33294     setMonth : function(v)
33295     {
33296         this.monthField.setValue(v, true);
33297         this.inputEl.dom.value = this.getValue();
33298         this.validate();
33299         return;
33300     },
33301     
33302     setYear : function(v)
33303     {
33304         this.yearField.setValue(v);
33305         this.inputEl.dom.value = this.getValue();
33306         this.validate();
33307         return;
33308     },
33309     
33310     getDay : function()
33311     {
33312         return this.dayField.getValue();
33313     },
33314     
33315     getMonth : function()
33316     {
33317         return this.monthField.getValue();
33318     },
33319     
33320     getYear : function()
33321     {
33322         return this.yearField.getValue();
33323     },
33324     
33325     getValue : function()
33326     {
33327         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33328         
33329         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33330         
33331         return date;
33332     },
33333     
33334     reset : function()
33335     {
33336         this.setDay('');
33337         this.setMonth('');
33338         this.setYear('');
33339         this.inputEl.dom.value = '';
33340         this.validate();
33341         return;
33342     },
33343     
33344     validate : function()
33345     {
33346         var d = this.dayField.validate();
33347         var m = this.monthField.validate();
33348         var y = this.yearField.validate();
33349         
33350         var valid = true;
33351         
33352         if(
33353                 (!this.dayAllowBlank && !d) ||
33354                 (!this.monthAllowBlank && !m) ||
33355                 (!this.yearAllowBlank && !y)
33356         ){
33357             valid = false;
33358         }
33359         
33360         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33361             return valid;
33362         }
33363         
33364         if(valid){
33365             this.markValid();
33366             return valid;
33367         }
33368         
33369         this.markInvalid();
33370         
33371         return valid;
33372     },
33373     
33374     markValid : function()
33375     {
33376         
33377         var label = this.el.select('label', true).first();
33378         var icon = this.el.select('i.fa-star', true).first();
33379
33380         if(label && icon){
33381             icon.remove();
33382         }
33383         
33384         this.fireEvent('valid', this);
33385     },
33386     
33387      /**
33388      * Mark this field as invalid
33389      * @param {String} msg The validation message
33390      */
33391     markInvalid : function(msg)
33392     {
33393         
33394         var label = this.el.select('label', true).first();
33395         var icon = this.el.select('i.fa-star', true).first();
33396
33397         if(label && !icon){
33398             this.el.select('.roo-date-split-field-label', true).createChild({
33399                 tag : 'i',
33400                 cls : 'text-danger fa fa-lg fa-star',
33401                 tooltip : 'This field is required',
33402                 style : 'margin-right:5px;'
33403             }, label, true);
33404         }
33405         
33406         this.fireEvent('invalid', this, msg);
33407     },
33408     
33409     clearInvalid : function()
33410     {
33411         var label = this.el.select('label', true).first();
33412         var icon = this.el.select('i.fa-star', true).first();
33413
33414         if(label && icon){
33415             icon.remove();
33416         }
33417         
33418         this.fireEvent('valid', this);
33419     },
33420     
33421     getName: function()
33422     {
33423         return this.name;
33424     }
33425     
33426 });
33427
33428  /**
33429  *
33430  * This is based on 
33431  * http://masonry.desandro.com
33432  *
33433  * The idea is to render all the bricks based on vertical width...
33434  *
33435  * The original code extends 'outlayer' - we might need to use that....
33436  * 
33437  */
33438
33439
33440 /**
33441  * @class Roo.bootstrap.LayoutMasonry
33442  * @extends Roo.bootstrap.Component
33443  * Bootstrap Layout Masonry class
33444  * 
33445  * @constructor
33446  * Create a new Element
33447  * @param {Object} config The config object
33448  */
33449
33450 Roo.bootstrap.LayoutMasonry = function(config){
33451     
33452     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33453     
33454     this.bricks = [];
33455     
33456     Roo.bootstrap.LayoutMasonry.register(this);
33457     
33458     this.addEvents({
33459         // raw events
33460         /**
33461          * @event layout
33462          * Fire after layout the items
33463          * @param {Roo.bootstrap.LayoutMasonry} this
33464          * @param {Roo.EventObject} e
33465          */
33466         "layout" : true
33467     });
33468     
33469 };
33470
33471 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33472     
33473     /**
33474      * @cfg {Boolean} isLayoutInstant = no animation?
33475      */   
33476     isLayoutInstant : false, // needed?
33477    
33478     /**
33479      * @cfg {Number} boxWidth  width of the columns
33480      */   
33481     boxWidth : 450,
33482     
33483       /**
33484      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33485      */   
33486     boxHeight : 0,
33487     
33488     /**
33489      * @cfg {Number} padWidth padding below box..
33490      */   
33491     padWidth : 10, 
33492     
33493     /**
33494      * @cfg {Number} gutter gutter width..
33495      */   
33496     gutter : 10,
33497     
33498      /**
33499      * @cfg {Number} maxCols maximum number of columns
33500      */   
33501     
33502     maxCols: 0,
33503     
33504     /**
33505      * @cfg {Boolean} isAutoInitial defalut true
33506      */   
33507     isAutoInitial : true, 
33508     
33509     containerWidth: 0,
33510     
33511     /**
33512      * @cfg {Boolean} isHorizontal defalut false
33513      */   
33514     isHorizontal : false, 
33515
33516     currentSize : null,
33517     
33518     tag: 'div',
33519     
33520     cls: '',
33521     
33522     bricks: null, //CompositeElement
33523     
33524     cols : 1,
33525     
33526     _isLayoutInited : false,
33527     
33528 //    isAlternative : false, // only use for vertical layout...
33529     
33530     /**
33531      * @cfg {Number} alternativePadWidth padding below box..
33532      */   
33533     alternativePadWidth : 50,
33534     
33535     selectedBrick : [],
33536     
33537     getAutoCreate : function(){
33538         
33539         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33540         
33541         var cfg = {
33542             tag: this.tag,
33543             cls: 'blog-masonary-wrapper ' + this.cls,
33544             cn : {
33545                 cls : 'mas-boxes masonary'
33546             }
33547         };
33548         
33549         return cfg;
33550     },
33551     
33552     getChildContainer: function( )
33553     {
33554         if (this.boxesEl) {
33555             return this.boxesEl;
33556         }
33557         
33558         this.boxesEl = this.el.select('.mas-boxes').first();
33559         
33560         return this.boxesEl;
33561     },
33562     
33563     
33564     initEvents : function()
33565     {
33566         var _this = this;
33567         
33568         if(this.isAutoInitial){
33569             Roo.log('hook children rendered');
33570             this.on('childrenrendered', function() {
33571                 Roo.log('children rendered');
33572                 _this.initial();
33573             } ,this);
33574         }
33575     },
33576     
33577     initial : function()
33578     {
33579         this.selectedBrick = [];
33580         
33581         this.currentSize = this.el.getBox(true);
33582         
33583         Roo.EventManager.onWindowResize(this.resize, this); 
33584
33585         if(!this.isAutoInitial){
33586             this.layout();
33587             return;
33588         }
33589         
33590         this.layout();
33591         
33592         return;
33593         //this.layout.defer(500,this);
33594         
33595     },
33596     
33597     resize : function()
33598     {
33599         var cs = this.el.getBox(true);
33600         
33601         if (
33602                 this.currentSize.width == cs.width && 
33603                 this.currentSize.x == cs.x && 
33604                 this.currentSize.height == cs.height && 
33605                 this.currentSize.y == cs.y 
33606         ) {
33607             Roo.log("no change in with or X or Y");
33608             return;
33609         }
33610         
33611         this.currentSize = cs;
33612         
33613         this.layout();
33614         
33615     },
33616     
33617     layout : function()
33618     {   
33619         this._resetLayout();
33620         
33621         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33622         
33623         this.layoutItems( isInstant );
33624       
33625         this._isLayoutInited = true;
33626         
33627         this.fireEvent('layout', this);
33628         
33629     },
33630     
33631     _resetLayout : function()
33632     {
33633         if(this.isHorizontal){
33634             this.horizontalMeasureColumns();
33635             return;
33636         }
33637         
33638         this.verticalMeasureColumns();
33639         
33640     },
33641     
33642     verticalMeasureColumns : function()
33643     {
33644         this.getContainerWidth();
33645         
33646 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33647 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33648 //            return;
33649 //        }
33650         
33651         var boxWidth = this.boxWidth + this.padWidth;
33652         
33653         if(this.containerWidth < this.boxWidth){
33654             boxWidth = this.containerWidth
33655         }
33656         
33657         var containerWidth = this.containerWidth;
33658         
33659         var cols = Math.floor(containerWidth / boxWidth);
33660         
33661         this.cols = Math.max( cols, 1 );
33662         
33663         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33664         
33665         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33666         
33667         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33668         
33669         this.colWidth = boxWidth + avail - this.padWidth;
33670         
33671         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33672         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33673     },
33674     
33675     horizontalMeasureColumns : function()
33676     {
33677         this.getContainerWidth();
33678         
33679         var boxWidth = this.boxWidth;
33680         
33681         if(this.containerWidth < boxWidth){
33682             boxWidth = this.containerWidth;
33683         }
33684         
33685         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33686         
33687         this.el.setHeight(boxWidth);
33688         
33689     },
33690     
33691     getContainerWidth : function()
33692     {
33693         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33694     },
33695     
33696     layoutItems : function( isInstant )
33697     {
33698         Roo.log(this.bricks);
33699         
33700         var items = Roo.apply([], this.bricks);
33701         
33702         if(this.isHorizontal){
33703             this._horizontalLayoutItems( items , isInstant );
33704             return;
33705         }
33706         
33707 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33708 //            this._verticalAlternativeLayoutItems( items , isInstant );
33709 //            return;
33710 //        }
33711         
33712         this._verticalLayoutItems( items , isInstant );
33713         
33714     },
33715     
33716     _verticalLayoutItems : function ( items , isInstant)
33717     {
33718         if ( !items || !items.length ) {
33719             return;
33720         }
33721         
33722         var standard = [
33723             ['xs', 'xs', 'xs', 'tall'],
33724             ['xs', 'xs', 'tall'],
33725             ['xs', 'xs', 'sm'],
33726             ['xs', 'xs', 'xs'],
33727             ['xs', 'tall'],
33728             ['xs', 'sm'],
33729             ['xs', 'xs'],
33730             ['xs'],
33731             
33732             ['sm', 'xs', 'xs'],
33733             ['sm', 'xs'],
33734             ['sm'],
33735             
33736             ['tall', 'xs', 'xs', 'xs'],
33737             ['tall', 'xs', 'xs'],
33738             ['tall', 'xs'],
33739             ['tall']
33740             
33741         ];
33742         
33743         var queue = [];
33744         
33745         var boxes = [];
33746         
33747         var box = [];
33748         
33749         Roo.each(items, function(item, k){
33750             
33751             switch (item.size) {
33752                 // these layouts take up a full box,
33753                 case 'md' :
33754                 case 'md-left' :
33755                 case 'md-right' :
33756                 case 'wide' :
33757                     
33758                     if(box.length){
33759                         boxes.push(box);
33760                         box = [];
33761                     }
33762                     
33763                     boxes.push([item]);
33764                     
33765                     break;
33766                     
33767                 case 'xs' :
33768                 case 'sm' :
33769                 case 'tall' :
33770                     
33771                     box.push(item);
33772                     
33773                     break;
33774                 default :
33775                     break;
33776                     
33777             }
33778             
33779         }, this);
33780         
33781         if(box.length){
33782             boxes.push(box);
33783             box = [];
33784         }
33785         
33786         var filterPattern = function(box, length)
33787         {
33788             if(!box.length){
33789                 return;
33790             }
33791             
33792             var match = false;
33793             
33794             var pattern = box.slice(0, length);
33795             
33796             var format = [];
33797             
33798             Roo.each(pattern, function(i){
33799                 format.push(i.size);
33800             }, this);
33801             
33802             Roo.each(standard, function(s){
33803                 
33804                 if(String(s) != String(format)){
33805                     return;
33806                 }
33807                 
33808                 match = true;
33809                 return false;
33810                 
33811             }, this);
33812             
33813             if(!match && length == 1){
33814                 return;
33815             }
33816             
33817             if(!match){
33818                 filterPattern(box, length - 1);
33819                 return;
33820             }
33821                 
33822             queue.push(pattern);
33823
33824             box = box.slice(length, box.length);
33825
33826             filterPattern(box, 4);
33827
33828             return;
33829             
33830         }
33831         
33832         Roo.each(boxes, function(box, k){
33833             
33834             if(!box.length){
33835                 return;
33836             }
33837             
33838             if(box.length == 1){
33839                 queue.push(box);
33840                 return;
33841             }
33842             
33843             filterPattern(box, 4);
33844             
33845         }, this);
33846         
33847         this._processVerticalLayoutQueue( queue, isInstant );
33848         
33849     },
33850     
33851 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33852 //    {
33853 //        if ( !items || !items.length ) {
33854 //            return;
33855 //        }
33856 //
33857 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33858 //        
33859 //    },
33860     
33861     _horizontalLayoutItems : function ( items , isInstant)
33862     {
33863         if ( !items || !items.length || items.length < 3) {
33864             return;
33865         }
33866         
33867         items.reverse();
33868         
33869         var eItems = items.slice(0, 3);
33870         
33871         items = items.slice(3, items.length);
33872         
33873         var standard = [
33874             ['xs', 'xs', 'xs', 'wide'],
33875             ['xs', 'xs', 'wide'],
33876             ['xs', 'xs', 'sm'],
33877             ['xs', 'xs', 'xs'],
33878             ['xs', 'wide'],
33879             ['xs', 'sm'],
33880             ['xs', 'xs'],
33881             ['xs'],
33882             
33883             ['sm', 'xs', 'xs'],
33884             ['sm', 'xs'],
33885             ['sm'],
33886             
33887             ['wide', 'xs', 'xs', 'xs'],
33888             ['wide', 'xs', 'xs'],
33889             ['wide', 'xs'],
33890             ['wide'],
33891             
33892             ['wide-thin']
33893         ];
33894         
33895         var queue = [];
33896         
33897         var boxes = [];
33898         
33899         var box = [];
33900         
33901         Roo.each(items, function(item, k){
33902             
33903             switch (item.size) {
33904                 case 'md' :
33905                 case 'md-left' :
33906                 case 'md-right' :
33907                 case 'tall' :
33908                     
33909                     if(box.length){
33910                         boxes.push(box);
33911                         box = [];
33912                     }
33913                     
33914                     boxes.push([item]);
33915                     
33916                     break;
33917                     
33918                 case 'xs' :
33919                 case 'sm' :
33920                 case 'wide' :
33921                 case 'wide-thin' :
33922                     
33923                     box.push(item);
33924                     
33925                     break;
33926                 default :
33927                     break;
33928                     
33929             }
33930             
33931         }, this);
33932         
33933         if(box.length){
33934             boxes.push(box);
33935             box = [];
33936         }
33937         
33938         var filterPattern = function(box, length)
33939         {
33940             if(!box.length){
33941                 return;
33942             }
33943             
33944             var match = false;
33945             
33946             var pattern = box.slice(0, length);
33947             
33948             var format = [];
33949             
33950             Roo.each(pattern, function(i){
33951                 format.push(i.size);
33952             }, this);
33953             
33954             Roo.each(standard, function(s){
33955                 
33956                 if(String(s) != String(format)){
33957                     return;
33958                 }
33959                 
33960                 match = true;
33961                 return false;
33962                 
33963             }, this);
33964             
33965             if(!match && length == 1){
33966                 return;
33967             }
33968             
33969             if(!match){
33970                 filterPattern(box, length - 1);
33971                 return;
33972             }
33973                 
33974             queue.push(pattern);
33975
33976             box = box.slice(length, box.length);
33977
33978             filterPattern(box, 4);
33979
33980             return;
33981             
33982         }
33983         
33984         Roo.each(boxes, function(box, k){
33985             
33986             if(!box.length){
33987                 return;
33988             }
33989             
33990             if(box.length == 1){
33991                 queue.push(box);
33992                 return;
33993             }
33994             
33995             filterPattern(box, 4);
33996             
33997         }, this);
33998         
33999         
34000         var prune = [];
34001         
34002         var pos = this.el.getBox(true);
34003         
34004         var minX = pos.x;
34005         
34006         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34007         
34008         var hit_end = false;
34009         
34010         Roo.each(queue, function(box){
34011             
34012             if(hit_end){
34013                 
34014                 Roo.each(box, function(b){
34015                 
34016                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34017                     b.el.hide();
34018
34019                 }, this);
34020
34021                 return;
34022             }
34023             
34024             var mx = 0;
34025             
34026             Roo.each(box, function(b){
34027                 
34028                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34029                 b.el.show();
34030
34031                 mx = Math.max(mx, b.x);
34032                 
34033             }, this);
34034             
34035             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34036             
34037             if(maxX < minX){
34038                 
34039                 Roo.each(box, function(b){
34040                 
34041                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34042                     b.el.hide();
34043                     
34044                 }, this);
34045                 
34046                 hit_end = true;
34047                 
34048                 return;
34049             }
34050             
34051             prune.push(box);
34052             
34053         }, this);
34054         
34055         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34056     },
34057     
34058     /** Sets position of item in DOM
34059     * @param {Element} item
34060     * @param {Number} x - horizontal position
34061     * @param {Number} y - vertical position
34062     * @param {Boolean} isInstant - disables transitions
34063     */
34064     _processVerticalLayoutQueue : function( queue, isInstant )
34065     {
34066         var pos = this.el.getBox(true);
34067         var x = pos.x;
34068         var y = pos.y;
34069         var maxY = [];
34070         
34071         for (var i = 0; i < this.cols; i++){
34072             maxY[i] = pos.y;
34073         }
34074         
34075         Roo.each(queue, function(box, k){
34076             
34077             var col = k % this.cols;
34078             
34079             Roo.each(box, function(b,kk){
34080                 
34081                 b.el.position('absolute');
34082                 
34083                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34084                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34085                 
34086                 if(b.size == 'md-left' || b.size == 'md-right'){
34087                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34088                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34089                 }
34090                 
34091                 b.el.setWidth(width);
34092                 b.el.setHeight(height);
34093                 // iframe?
34094                 b.el.select('iframe',true).setSize(width,height);
34095                 
34096             }, this);
34097             
34098             for (var i = 0; i < this.cols; i++){
34099                 
34100                 if(maxY[i] < maxY[col]){
34101                     col = i;
34102                     continue;
34103                 }
34104                 
34105                 col = Math.min(col, i);
34106                 
34107             }
34108             
34109             x = pos.x + col * (this.colWidth + this.padWidth);
34110             
34111             y = maxY[col];
34112             
34113             var positions = [];
34114             
34115             switch (box.length){
34116                 case 1 :
34117                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34118                     break;
34119                 case 2 :
34120                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34121                     break;
34122                 case 3 :
34123                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34124                     break;
34125                 case 4 :
34126                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34127                     break;
34128                 default :
34129                     break;
34130             }
34131             
34132             Roo.each(box, function(b,kk){
34133                 
34134                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34135                 
34136                 var sz = b.el.getSize();
34137                 
34138                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34139                 
34140             }, this);
34141             
34142         }, this);
34143         
34144         var mY = 0;
34145         
34146         for (var i = 0; i < this.cols; i++){
34147             mY = Math.max(mY, maxY[i]);
34148         }
34149         
34150         this.el.setHeight(mY - pos.y);
34151         
34152     },
34153     
34154 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34155 //    {
34156 //        var pos = this.el.getBox(true);
34157 //        var x = pos.x;
34158 //        var y = pos.y;
34159 //        var maxX = pos.right;
34160 //        
34161 //        var maxHeight = 0;
34162 //        
34163 //        Roo.each(items, function(item, k){
34164 //            
34165 //            var c = k % 2;
34166 //            
34167 //            item.el.position('absolute');
34168 //                
34169 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34170 //
34171 //            item.el.setWidth(width);
34172 //
34173 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34174 //
34175 //            item.el.setHeight(height);
34176 //            
34177 //            if(c == 0){
34178 //                item.el.setXY([x, y], isInstant ? false : true);
34179 //            } else {
34180 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34181 //            }
34182 //            
34183 //            y = y + height + this.alternativePadWidth;
34184 //            
34185 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34186 //            
34187 //        }, this);
34188 //        
34189 //        this.el.setHeight(maxHeight);
34190 //        
34191 //    },
34192     
34193     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34194     {
34195         var pos = this.el.getBox(true);
34196         
34197         var minX = pos.x;
34198         var minY = pos.y;
34199         
34200         var maxX = pos.right;
34201         
34202         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34203         
34204         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34205         
34206         Roo.each(queue, function(box, k){
34207             
34208             Roo.each(box, function(b, kk){
34209                 
34210                 b.el.position('absolute');
34211                 
34212                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34213                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34214                 
34215                 if(b.size == 'md-left' || b.size == 'md-right'){
34216                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34217                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34218                 }
34219                 
34220                 b.el.setWidth(width);
34221                 b.el.setHeight(height);
34222                 
34223             }, this);
34224             
34225             if(!box.length){
34226                 return;
34227             }
34228             
34229             var positions = [];
34230             
34231             switch (box.length){
34232                 case 1 :
34233                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34234                     break;
34235                 case 2 :
34236                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34237                     break;
34238                 case 3 :
34239                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34240                     break;
34241                 case 4 :
34242                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34243                     break;
34244                 default :
34245                     break;
34246             }
34247             
34248             Roo.each(box, function(b,kk){
34249                 
34250                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34251                 
34252                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34253                 
34254             }, this);
34255             
34256         }, this);
34257         
34258     },
34259     
34260     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34261     {
34262         Roo.each(eItems, function(b,k){
34263             
34264             b.size = (k == 0) ? 'sm' : 'xs';
34265             b.x = (k == 0) ? 2 : 1;
34266             b.y = (k == 0) ? 2 : 1;
34267             
34268             b.el.position('absolute');
34269             
34270             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34271                 
34272             b.el.setWidth(width);
34273             
34274             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34275             
34276             b.el.setHeight(height);
34277             
34278         }, this);
34279
34280         var positions = [];
34281         
34282         positions.push({
34283             x : maxX - this.unitWidth * 2 - this.gutter,
34284             y : minY
34285         });
34286         
34287         positions.push({
34288             x : maxX - this.unitWidth,
34289             y : minY + (this.unitWidth + this.gutter) * 2
34290         });
34291         
34292         positions.push({
34293             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34294             y : minY
34295         });
34296         
34297         Roo.each(eItems, function(b,k){
34298             
34299             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34300
34301         }, this);
34302         
34303     },
34304     
34305     getVerticalOneBoxColPositions : function(x, y, box)
34306     {
34307         var pos = [];
34308         
34309         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34310         
34311         if(box[0].size == 'md-left'){
34312             rand = 0;
34313         }
34314         
34315         if(box[0].size == 'md-right'){
34316             rand = 1;
34317         }
34318         
34319         pos.push({
34320             x : x + (this.unitWidth + this.gutter) * rand,
34321             y : y
34322         });
34323         
34324         return pos;
34325     },
34326     
34327     getVerticalTwoBoxColPositions : function(x, y, box)
34328     {
34329         var pos = [];
34330         
34331         if(box[0].size == 'xs'){
34332             
34333             pos.push({
34334                 x : x,
34335                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34336             });
34337
34338             pos.push({
34339                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34340                 y : y
34341             });
34342             
34343             return pos;
34344             
34345         }
34346         
34347         pos.push({
34348             x : x,
34349             y : y
34350         });
34351
34352         pos.push({
34353             x : x + (this.unitWidth + this.gutter) * 2,
34354             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34355         });
34356         
34357         return pos;
34358         
34359     },
34360     
34361     getVerticalThreeBoxColPositions : function(x, y, box)
34362     {
34363         var pos = [];
34364         
34365         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34366             
34367             pos.push({
34368                 x : x,
34369                 y : y
34370             });
34371
34372             pos.push({
34373                 x : x + (this.unitWidth + this.gutter) * 1,
34374                 y : y
34375             });
34376             
34377             pos.push({
34378                 x : x + (this.unitWidth + this.gutter) * 2,
34379                 y : y
34380             });
34381             
34382             return pos;
34383             
34384         }
34385         
34386         if(box[0].size == 'xs' && box[1].size == 'xs'){
34387             
34388             pos.push({
34389                 x : x,
34390                 y : y
34391             });
34392
34393             pos.push({
34394                 x : x,
34395                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34396             });
34397             
34398             pos.push({
34399                 x : x + (this.unitWidth + this.gutter) * 1,
34400                 y : y
34401             });
34402             
34403             return pos;
34404             
34405         }
34406         
34407         pos.push({
34408             x : x,
34409             y : y
34410         });
34411
34412         pos.push({
34413             x : x + (this.unitWidth + this.gutter) * 2,
34414             y : y
34415         });
34416
34417         pos.push({
34418             x : x + (this.unitWidth + this.gutter) * 2,
34419             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34420         });
34421             
34422         return pos;
34423         
34424     },
34425     
34426     getVerticalFourBoxColPositions : function(x, y, box)
34427     {
34428         var pos = [];
34429         
34430         if(box[0].size == 'xs'){
34431             
34432             pos.push({
34433                 x : x,
34434                 y : y
34435             });
34436
34437             pos.push({
34438                 x : x,
34439                 y : y + (this.unitHeight + this.gutter) * 1
34440             });
34441             
34442             pos.push({
34443                 x : x,
34444                 y : y + (this.unitHeight + this.gutter) * 2
34445             });
34446             
34447             pos.push({
34448                 x : x + (this.unitWidth + this.gutter) * 1,
34449                 y : y
34450             });
34451             
34452             return pos;
34453             
34454         }
34455         
34456         pos.push({
34457             x : x,
34458             y : y
34459         });
34460
34461         pos.push({
34462             x : x + (this.unitWidth + this.gutter) * 2,
34463             y : y
34464         });
34465
34466         pos.push({
34467             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34468             y : y + (this.unitHeight + this.gutter) * 1
34469         });
34470
34471         pos.push({
34472             x : x + (this.unitWidth + this.gutter) * 2,
34473             y : y + (this.unitWidth + this.gutter) * 2
34474         });
34475
34476         return pos;
34477         
34478     },
34479     
34480     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34481     {
34482         var pos = [];
34483         
34484         if(box[0].size == 'md-left'){
34485             pos.push({
34486                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34487                 y : minY
34488             });
34489             
34490             return pos;
34491         }
34492         
34493         if(box[0].size == 'md-right'){
34494             pos.push({
34495                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34496                 y : minY + (this.unitWidth + this.gutter) * 1
34497             });
34498             
34499             return pos;
34500         }
34501         
34502         var rand = Math.floor(Math.random() * (4 - box[0].y));
34503         
34504         pos.push({
34505             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34506             y : minY + (this.unitWidth + this.gutter) * rand
34507         });
34508         
34509         return pos;
34510         
34511     },
34512     
34513     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34514     {
34515         var pos = [];
34516         
34517         if(box[0].size == 'xs'){
34518             
34519             pos.push({
34520                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34521                 y : minY
34522             });
34523
34524             pos.push({
34525                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34526                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34527             });
34528             
34529             return pos;
34530             
34531         }
34532         
34533         pos.push({
34534             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34535             y : minY
34536         });
34537
34538         pos.push({
34539             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34540             y : minY + (this.unitWidth + this.gutter) * 2
34541         });
34542         
34543         return pos;
34544         
34545     },
34546     
34547     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34548     {
34549         var pos = [];
34550         
34551         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34552             
34553             pos.push({
34554                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34555                 y : minY
34556             });
34557
34558             pos.push({
34559                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34560                 y : minY + (this.unitWidth + this.gutter) * 1
34561             });
34562             
34563             pos.push({
34564                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34565                 y : minY + (this.unitWidth + this.gutter) * 2
34566             });
34567             
34568             return pos;
34569             
34570         }
34571         
34572         if(box[0].size == 'xs' && box[1].size == 'xs'){
34573             
34574             pos.push({
34575                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34576                 y : minY
34577             });
34578
34579             pos.push({
34580                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34581                 y : minY
34582             });
34583             
34584             pos.push({
34585                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34586                 y : minY + (this.unitWidth + this.gutter) * 1
34587             });
34588             
34589             return pos;
34590             
34591         }
34592         
34593         pos.push({
34594             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34595             y : minY
34596         });
34597
34598         pos.push({
34599             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34600             y : minY + (this.unitWidth + this.gutter) * 2
34601         });
34602
34603         pos.push({
34604             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34605             y : minY + (this.unitWidth + this.gutter) * 2
34606         });
34607             
34608         return pos;
34609         
34610     },
34611     
34612     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34613     {
34614         var pos = [];
34615         
34616         if(box[0].size == 'xs'){
34617             
34618             pos.push({
34619                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34620                 y : minY
34621             });
34622
34623             pos.push({
34624                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34625                 y : minY
34626             });
34627             
34628             pos.push({
34629                 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),
34630                 y : minY
34631             });
34632             
34633             pos.push({
34634                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34635                 y : minY + (this.unitWidth + this.gutter) * 1
34636             });
34637             
34638             return pos;
34639             
34640         }
34641         
34642         pos.push({
34643             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34644             y : minY
34645         });
34646         
34647         pos.push({
34648             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34649             y : minY + (this.unitWidth + this.gutter) * 2
34650         });
34651         
34652         pos.push({
34653             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34654             y : minY + (this.unitWidth + this.gutter) * 2
34655         });
34656         
34657         pos.push({
34658             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),
34659             y : minY + (this.unitWidth + this.gutter) * 2
34660         });
34661
34662         return pos;
34663         
34664     },
34665     
34666     /**
34667     * remove a Masonry Brick
34668     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34669     */
34670     removeBrick : function(brick_id)
34671     {
34672         if (!brick_id) {
34673             return;
34674         }
34675         
34676         for (var i = 0; i<this.bricks.length; i++) {
34677             if (this.bricks[i].id == brick_id) {
34678                 this.bricks.splice(i,1);
34679                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34680                 this.initial();
34681             }
34682         }
34683     },
34684     
34685     /**
34686     * adds a Masonry Brick
34687     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34688     */
34689     addBrick : function(cfg)
34690     {
34691         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34692         //this.register(cn);
34693         cn.parentId = this.id;
34694         cn.render(this.el);
34695         return cn;
34696     },
34697     
34698     /**
34699     * register a Masonry Brick
34700     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34701     */
34702     
34703     register : function(brick)
34704     {
34705         this.bricks.push(brick);
34706         brick.masonryId = this.id;
34707     },
34708     
34709     /**
34710     * clear all the Masonry Brick
34711     */
34712     clearAll : function()
34713     {
34714         this.bricks = [];
34715         //this.getChildContainer().dom.innerHTML = "";
34716         this.el.dom.innerHTML = '';
34717     },
34718     
34719     getSelected : function()
34720     {
34721         if (!this.selectedBrick) {
34722             return false;
34723         }
34724         
34725         return this.selectedBrick;
34726     }
34727 });
34728
34729 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34730     
34731     groups: {},
34732      /**
34733     * register a Masonry Layout
34734     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34735     */
34736     
34737     register : function(layout)
34738     {
34739         this.groups[layout.id] = layout;
34740     },
34741     /**
34742     * fetch a  Masonry Layout based on the masonry layout ID
34743     * @param {string} the masonry layout to add
34744     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34745     */
34746     
34747     get: function(layout_id) {
34748         if (typeof(this.groups[layout_id]) == 'undefined') {
34749             return false;
34750         }
34751         return this.groups[layout_id] ;
34752     }
34753     
34754     
34755     
34756 });
34757
34758  
34759
34760  /**
34761  *
34762  * This is based on 
34763  * http://masonry.desandro.com
34764  *
34765  * The idea is to render all the bricks based on vertical width...
34766  *
34767  * The original code extends 'outlayer' - we might need to use that....
34768  * 
34769  */
34770
34771
34772 /**
34773  * @class Roo.bootstrap.LayoutMasonryAuto
34774  * @extends Roo.bootstrap.Component
34775  * Bootstrap Layout Masonry class
34776  * 
34777  * @constructor
34778  * Create a new Element
34779  * @param {Object} config The config object
34780  */
34781
34782 Roo.bootstrap.LayoutMasonryAuto = function(config){
34783     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34784 };
34785
34786 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34787     
34788       /**
34789      * @cfg {Boolean} isFitWidth  - resize the width..
34790      */   
34791     isFitWidth : false,  // options..
34792     /**
34793      * @cfg {Boolean} isOriginLeft = left align?
34794      */   
34795     isOriginLeft : true,
34796     /**
34797      * @cfg {Boolean} isOriginTop = top align?
34798      */   
34799     isOriginTop : false,
34800     /**
34801      * @cfg {Boolean} isLayoutInstant = no animation?
34802      */   
34803     isLayoutInstant : false, // needed?
34804     /**
34805      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34806      */   
34807     isResizingContainer : true,
34808     /**
34809      * @cfg {Number} columnWidth  width of the columns 
34810      */   
34811     
34812     columnWidth : 0,
34813     
34814     /**
34815      * @cfg {Number} maxCols maximum number of columns
34816      */   
34817     
34818     maxCols: 0,
34819     /**
34820      * @cfg {Number} padHeight padding below box..
34821      */   
34822     
34823     padHeight : 10, 
34824     
34825     /**
34826      * @cfg {Boolean} isAutoInitial defalut true
34827      */   
34828     
34829     isAutoInitial : true, 
34830     
34831     // private?
34832     gutter : 0,
34833     
34834     containerWidth: 0,
34835     initialColumnWidth : 0,
34836     currentSize : null,
34837     
34838     colYs : null, // array.
34839     maxY : 0,
34840     padWidth: 10,
34841     
34842     
34843     tag: 'div',
34844     cls: '',
34845     bricks: null, //CompositeElement
34846     cols : 0, // array?
34847     // element : null, // wrapped now this.el
34848     _isLayoutInited : null, 
34849     
34850     
34851     getAutoCreate : function(){
34852         
34853         var cfg = {
34854             tag: this.tag,
34855             cls: 'blog-masonary-wrapper ' + this.cls,
34856             cn : {
34857                 cls : 'mas-boxes masonary'
34858             }
34859         };
34860         
34861         return cfg;
34862     },
34863     
34864     getChildContainer: function( )
34865     {
34866         if (this.boxesEl) {
34867             return this.boxesEl;
34868         }
34869         
34870         this.boxesEl = this.el.select('.mas-boxes').first();
34871         
34872         return this.boxesEl;
34873     },
34874     
34875     
34876     initEvents : function()
34877     {
34878         var _this = this;
34879         
34880         if(this.isAutoInitial){
34881             Roo.log('hook children rendered');
34882             this.on('childrenrendered', function() {
34883                 Roo.log('children rendered');
34884                 _this.initial();
34885             } ,this);
34886         }
34887         
34888     },
34889     
34890     initial : function()
34891     {
34892         this.reloadItems();
34893
34894         this.currentSize = this.el.getBox(true);
34895
34896         /// was window resize... - let's see if this works..
34897         Roo.EventManager.onWindowResize(this.resize, this); 
34898
34899         if(!this.isAutoInitial){
34900             this.layout();
34901             return;
34902         }
34903         
34904         this.layout.defer(500,this);
34905     },
34906     
34907     reloadItems: function()
34908     {
34909         this.bricks = this.el.select('.masonry-brick', true);
34910         
34911         this.bricks.each(function(b) {
34912             //Roo.log(b.getSize());
34913             if (!b.attr('originalwidth')) {
34914                 b.attr('originalwidth',  b.getSize().width);
34915             }
34916             
34917         });
34918         
34919         Roo.log(this.bricks.elements.length);
34920     },
34921     
34922     resize : function()
34923     {
34924         Roo.log('resize');
34925         var cs = this.el.getBox(true);
34926         
34927         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34928             Roo.log("no change in with or X");
34929             return;
34930         }
34931         this.currentSize = cs;
34932         this.layout();
34933     },
34934     
34935     layout : function()
34936     {
34937          Roo.log('layout');
34938         this._resetLayout();
34939         //this._manageStamps();
34940       
34941         // don't animate first layout
34942         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34943         this.layoutItems( isInstant );
34944       
34945         // flag for initalized
34946         this._isLayoutInited = true;
34947     },
34948     
34949     layoutItems : function( isInstant )
34950     {
34951         //var items = this._getItemsForLayout( this.items );
34952         // original code supports filtering layout items.. we just ignore it..
34953         
34954         this._layoutItems( this.bricks , isInstant );
34955       
34956         this._postLayout();
34957     },
34958     _layoutItems : function ( items , isInstant)
34959     {
34960        //this.fireEvent( 'layout', this, items );
34961     
34962
34963         if ( !items || !items.elements.length ) {
34964           // no items, emit event with empty array
34965             return;
34966         }
34967
34968         var queue = [];
34969         items.each(function(item) {
34970             Roo.log("layout item");
34971             Roo.log(item);
34972             // get x/y object from method
34973             var position = this._getItemLayoutPosition( item );
34974             // enqueue
34975             position.item = item;
34976             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34977             queue.push( position );
34978         }, this);
34979       
34980         this._processLayoutQueue( queue );
34981     },
34982     /** Sets position of item in DOM
34983     * @param {Element} item
34984     * @param {Number} x - horizontal position
34985     * @param {Number} y - vertical position
34986     * @param {Boolean} isInstant - disables transitions
34987     */
34988     _processLayoutQueue : function( queue )
34989     {
34990         for ( var i=0, len = queue.length; i < len; i++ ) {
34991             var obj = queue[i];
34992             obj.item.position('absolute');
34993             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34994         }
34995     },
34996       
34997     
34998     /**
34999     * Any logic you want to do after each layout,
35000     * i.e. size the container
35001     */
35002     _postLayout : function()
35003     {
35004         this.resizeContainer();
35005     },
35006     
35007     resizeContainer : function()
35008     {
35009         if ( !this.isResizingContainer ) {
35010             return;
35011         }
35012         var size = this._getContainerSize();
35013         if ( size ) {
35014             this.el.setSize(size.width,size.height);
35015             this.boxesEl.setSize(size.width,size.height);
35016         }
35017     },
35018     
35019     
35020     
35021     _resetLayout : function()
35022     {
35023         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35024         this.colWidth = this.el.getWidth();
35025         //this.gutter = this.el.getWidth(); 
35026         
35027         this.measureColumns();
35028
35029         // reset column Y
35030         var i = this.cols;
35031         this.colYs = [];
35032         while (i--) {
35033             this.colYs.push( 0 );
35034         }
35035     
35036         this.maxY = 0;
35037     },
35038
35039     measureColumns : function()
35040     {
35041         this.getContainerWidth();
35042       // if columnWidth is 0, default to outerWidth of first item
35043         if ( !this.columnWidth ) {
35044             var firstItem = this.bricks.first();
35045             Roo.log(firstItem);
35046             this.columnWidth  = this.containerWidth;
35047             if (firstItem && firstItem.attr('originalwidth') ) {
35048                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35049             }
35050             // columnWidth fall back to item of first element
35051             Roo.log("set column width?");
35052                         this.initialColumnWidth = this.columnWidth  ;
35053
35054             // if first elem has no width, default to size of container
35055             
35056         }
35057         
35058         
35059         if (this.initialColumnWidth) {
35060             this.columnWidth = this.initialColumnWidth;
35061         }
35062         
35063         
35064             
35065         // column width is fixed at the top - however if container width get's smaller we should
35066         // reduce it...
35067         
35068         // this bit calcs how man columns..
35069             
35070         var columnWidth = this.columnWidth += this.gutter;
35071       
35072         // calculate columns
35073         var containerWidth = this.containerWidth + this.gutter;
35074         
35075         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35076         // fix rounding errors, typically with gutters
35077         var excess = columnWidth - containerWidth % columnWidth;
35078         
35079         
35080         // if overshoot is less than a pixel, round up, otherwise floor it
35081         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35082         cols = Math[ mathMethod ]( cols );
35083         this.cols = Math.max( cols, 1 );
35084         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35085         
35086          // padding positioning..
35087         var totalColWidth = this.cols * this.columnWidth;
35088         var padavail = this.containerWidth - totalColWidth;
35089         // so for 2 columns - we need 3 'pads'
35090         
35091         var padNeeded = (1+this.cols) * this.padWidth;
35092         
35093         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35094         
35095         this.columnWidth += padExtra
35096         //this.padWidth = Math.floor(padavail /  ( this.cols));
35097         
35098         // adjust colum width so that padding is fixed??
35099         
35100         // we have 3 columns ... total = width * 3
35101         // we have X left over... that should be used by 
35102         
35103         //if (this.expandC) {
35104             
35105         //}
35106         
35107         
35108         
35109     },
35110     
35111     getContainerWidth : function()
35112     {
35113        /* // container is parent if fit width
35114         var container = this.isFitWidth ? this.element.parentNode : this.element;
35115         // check that this.size and size are there
35116         // IE8 triggers resize on body size change, so they might not be
35117         
35118         var size = getSize( container );  //FIXME
35119         this.containerWidth = size && size.innerWidth; //FIXME
35120         */
35121          
35122         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35123         
35124     },
35125     
35126     _getItemLayoutPosition : function( item )  // what is item?
35127     {
35128         // we resize the item to our columnWidth..
35129       
35130         item.setWidth(this.columnWidth);
35131         item.autoBoxAdjust  = false;
35132         
35133         var sz = item.getSize();
35134  
35135         // how many columns does this brick span
35136         var remainder = this.containerWidth % this.columnWidth;
35137         
35138         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35139         // round if off by 1 pixel, otherwise use ceil
35140         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35141         colSpan = Math.min( colSpan, this.cols );
35142         
35143         // normally this should be '1' as we dont' currently allow multi width columns..
35144         
35145         var colGroup = this._getColGroup( colSpan );
35146         // get the minimum Y value from the columns
35147         var minimumY = Math.min.apply( Math, colGroup );
35148         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35149         
35150         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35151          
35152         // position the brick
35153         var position = {
35154             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35155             y: this.currentSize.y + minimumY + this.padHeight
35156         };
35157         
35158         Roo.log(position);
35159         // apply setHeight to necessary columns
35160         var setHeight = minimumY + sz.height + this.padHeight;
35161         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35162         
35163         var setSpan = this.cols + 1 - colGroup.length;
35164         for ( var i = 0; i < setSpan; i++ ) {
35165           this.colYs[ shortColIndex + i ] = setHeight ;
35166         }
35167       
35168         return position;
35169     },
35170     
35171     /**
35172      * @param {Number} colSpan - number of columns the element spans
35173      * @returns {Array} colGroup
35174      */
35175     _getColGroup : function( colSpan )
35176     {
35177         if ( colSpan < 2 ) {
35178           // if brick spans only one column, use all the column Ys
35179           return this.colYs;
35180         }
35181       
35182         var colGroup = [];
35183         // how many different places could this brick fit horizontally
35184         var groupCount = this.cols + 1 - colSpan;
35185         // for each group potential horizontal position
35186         for ( var i = 0; i < groupCount; i++ ) {
35187           // make an array of colY values for that one group
35188           var groupColYs = this.colYs.slice( i, i + colSpan );
35189           // and get the max value of the array
35190           colGroup[i] = Math.max.apply( Math, groupColYs );
35191         }
35192         return colGroup;
35193     },
35194     /*
35195     _manageStamp : function( stamp )
35196     {
35197         var stampSize =  stamp.getSize();
35198         var offset = stamp.getBox();
35199         // get the columns that this stamp affects
35200         var firstX = this.isOriginLeft ? offset.x : offset.right;
35201         var lastX = firstX + stampSize.width;
35202         var firstCol = Math.floor( firstX / this.columnWidth );
35203         firstCol = Math.max( 0, firstCol );
35204         
35205         var lastCol = Math.floor( lastX / this.columnWidth );
35206         // lastCol should not go over if multiple of columnWidth #425
35207         lastCol -= lastX % this.columnWidth ? 0 : 1;
35208         lastCol = Math.min( this.cols - 1, lastCol );
35209         
35210         // set colYs to bottom of the stamp
35211         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35212             stampSize.height;
35213             
35214         for ( var i = firstCol; i <= lastCol; i++ ) {
35215           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35216         }
35217     },
35218     */
35219     
35220     _getContainerSize : function()
35221     {
35222         this.maxY = Math.max.apply( Math, this.colYs );
35223         var size = {
35224             height: this.maxY
35225         };
35226       
35227         if ( this.isFitWidth ) {
35228             size.width = this._getContainerFitWidth();
35229         }
35230       
35231         return size;
35232     },
35233     
35234     _getContainerFitWidth : function()
35235     {
35236         var unusedCols = 0;
35237         // count unused columns
35238         var i = this.cols;
35239         while ( --i ) {
35240           if ( this.colYs[i] !== 0 ) {
35241             break;
35242           }
35243           unusedCols++;
35244         }
35245         // fit container to columns that have been used
35246         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35247     },
35248     
35249     needsResizeLayout : function()
35250     {
35251         var previousWidth = this.containerWidth;
35252         this.getContainerWidth();
35253         return previousWidth !== this.containerWidth;
35254     }
35255  
35256 });
35257
35258  
35259
35260  /*
35261  * - LGPL
35262  *
35263  * element
35264  * 
35265  */
35266
35267 /**
35268  * @class Roo.bootstrap.MasonryBrick
35269  * @extends Roo.bootstrap.Component
35270  * Bootstrap MasonryBrick class
35271  * 
35272  * @constructor
35273  * Create a new MasonryBrick
35274  * @param {Object} config The config object
35275  */
35276
35277 Roo.bootstrap.MasonryBrick = function(config){
35278     
35279     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35280     
35281     Roo.bootstrap.MasonryBrick.register(this);
35282     
35283     this.addEvents({
35284         // raw events
35285         /**
35286          * @event click
35287          * When a MasonryBrick is clcik
35288          * @param {Roo.bootstrap.MasonryBrick} this
35289          * @param {Roo.EventObject} e
35290          */
35291         "click" : true
35292     });
35293 };
35294
35295 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35296     
35297     /**
35298      * @cfg {String} title
35299      */   
35300     title : '',
35301     /**
35302      * @cfg {String} html
35303      */   
35304     html : '',
35305     /**
35306      * @cfg {String} bgimage
35307      */   
35308     bgimage : '',
35309     /**
35310      * @cfg {String} videourl
35311      */   
35312     videourl : '',
35313     /**
35314      * @cfg {String} cls
35315      */   
35316     cls : '',
35317     /**
35318      * @cfg {String} href
35319      */   
35320     href : '',
35321     /**
35322      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35323      */   
35324     size : 'xs',
35325     
35326     /**
35327      * @cfg {String} placetitle (center|bottom)
35328      */   
35329     placetitle : '',
35330     
35331     /**
35332      * @cfg {Boolean} isFitContainer defalut true
35333      */   
35334     isFitContainer : true, 
35335     
35336     /**
35337      * @cfg {Boolean} preventDefault defalut false
35338      */   
35339     preventDefault : false, 
35340     
35341     /**
35342      * @cfg {Boolean} inverse defalut false
35343      */   
35344     maskInverse : false, 
35345     
35346     getAutoCreate : function()
35347     {
35348         if(!this.isFitContainer){
35349             return this.getSplitAutoCreate();
35350         }
35351         
35352         var cls = 'masonry-brick masonry-brick-full';
35353         
35354         if(this.href.length){
35355             cls += ' masonry-brick-link';
35356         }
35357         
35358         if(this.bgimage.length){
35359             cls += ' masonry-brick-image';
35360         }
35361         
35362         if(this.maskInverse){
35363             cls += ' mask-inverse';
35364         }
35365         
35366         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35367             cls += ' enable-mask';
35368         }
35369         
35370         if(this.size){
35371             cls += ' masonry-' + this.size + '-brick';
35372         }
35373         
35374         if(this.placetitle.length){
35375             
35376             switch (this.placetitle) {
35377                 case 'center' :
35378                     cls += ' masonry-center-title';
35379                     break;
35380                 case 'bottom' :
35381                     cls += ' masonry-bottom-title';
35382                     break;
35383                 default:
35384                     break;
35385             }
35386             
35387         } else {
35388             if(!this.html.length && !this.bgimage.length){
35389                 cls += ' masonry-center-title';
35390             }
35391
35392             if(!this.html.length && this.bgimage.length){
35393                 cls += ' masonry-bottom-title';
35394             }
35395         }
35396         
35397         if(this.cls){
35398             cls += ' ' + this.cls;
35399         }
35400         
35401         var cfg = {
35402             tag: (this.href.length) ? 'a' : 'div',
35403             cls: cls,
35404             cn: [
35405                 {
35406                     tag: 'div',
35407                     cls: 'masonry-brick-mask'
35408                 },
35409                 {
35410                     tag: 'div',
35411                     cls: 'masonry-brick-paragraph',
35412                     cn: []
35413                 }
35414             ]
35415         };
35416         
35417         if(this.href.length){
35418             cfg.href = this.href;
35419         }
35420         
35421         var cn = cfg.cn[1].cn;
35422         
35423         if(this.title.length){
35424             cn.push({
35425                 tag: 'h4',
35426                 cls: 'masonry-brick-title',
35427                 html: this.title
35428             });
35429         }
35430         
35431         if(this.html.length){
35432             cn.push({
35433                 tag: 'p',
35434                 cls: 'masonry-brick-text',
35435                 html: this.html
35436             });
35437         }
35438         
35439         if (!this.title.length && !this.html.length) {
35440             cfg.cn[1].cls += ' hide';
35441         }
35442         
35443         if(this.bgimage.length){
35444             cfg.cn.push({
35445                 tag: 'img',
35446                 cls: 'masonry-brick-image-view',
35447                 src: this.bgimage
35448             });
35449         }
35450         
35451         if(this.videourl.length){
35452             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35453             // youtube support only?
35454             cfg.cn.push({
35455                 tag: 'iframe',
35456                 cls: 'masonry-brick-image-view',
35457                 src: vurl,
35458                 frameborder : 0,
35459                 allowfullscreen : true
35460             });
35461         }
35462         
35463         return cfg;
35464         
35465     },
35466     
35467     getSplitAutoCreate : function()
35468     {
35469         var cls = 'masonry-brick masonry-brick-split';
35470         
35471         if(this.href.length){
35472             cls += ' masonry-brick-link';
35473         }
35474         
35475         if(this.bgimage.length){
35476             cls += ' masonry-brick-image';
35477         }
35478         
35479         if(this.size){
35480             cls += ' masonry-' + this.size + '-brick';
35481         }
35482         
35483         switch (this.placetitle) {
35484             case 'center' :
35485                 cls += ' masonry-center-title';
35486                 break;
35487             case 'bottom' :
35488                 cls += ' masonry-bottom-title';
35489                 break;
35490             default:
35491                 if(!this.bgimage.length){
35492                     cls += ' masonry-center-title';
35493                 }
35494
35495                 if(this.bgimage.length){
35496                     cls += ' masonry-bottom-title';
35497                 }
35498                 break;
35499         }
35500         
35501         if(this.cls){
35502             cls += ' ' + this.cls;
35503         }
35504         
35505         var cfg = {
35506             tag: (this.href.length) ? 'a' : 'div',
35507             cls: cls,
35508             cn: [
35509                 {
35510                     tag: 'div',
35511                     cls: 'masonry-brick-split-head',
35512                     cn: [
35513                         {
35514                             tag: 'div',
35515                             cls: 'masonry-brick-paragraph',
35516                             cn: []
35517                         }
35518                     ]
35519                 },
35520                 {
35521                     tag: 'div',
35522                     cls: 'masonry-brick-split-body',
35523                     cn: []
35524                 }
35525             ]
35526         };
35527         
35528         if(this.href.length){
35529             cfg.href = this.href;
35530         }
35531         
35532         if(this.title.length){
35533             cfg.cn[0].cn[0].cn.push({
35534                 tag: 'h4',
35535                 cls: 'masonry-brick-title',
35536                 html: this.title
35537             });
35538         }
35539         
35540         if(this.html.length){
35541             cfg.cn[1].cn.push({
35542                 tag: 'p',
35543                 cls: 'masonry-brick-text',
35544                 html: this.html
35545             });
35546         }
35547
35548         if(this.bgimage.length){
35549             cfg.cn[0].cn.push({
35550                 tag: 'img',
35551                 cls: 'masonry-brick-image-view',
35552                 src: this.bgimage
35553             });
35554         }
35555         
35556         if(this.videourl.length){
35557             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35558             // youtube support only?
35559             cfg.cn[0].cn.cn.push({
35560                 tag: 'iframe',
35561                 cls: 'masonry-brick-image-view',
35562                 src: vurl,
35563                 frameborder : 0,
35564                 allowfullscreen : true
35565             });
35566         }
35567         
35568         return cfg;
35569     },
35570     
35571     initEvents: function() 
35572     {
35573         switch (this.size) {
35574             case 'xs' :
35575                 this.x = 1;
35576                 this.y = 1;
35577                 break;
35578             case 'sm' :
35579                 this.x = 2;
35580                 this.y = 2;
35581                 break;
35582             case 'md' :
35583             case 'md-left' :
35584             case 'md-right' :
35585                 this.x = 3;
35586                 this.y = 3;
35587                 break;
35588             case 'tall' :
35589                 this.x = 2;
35590                 this.y = 3;
35591                 break;
35592             case 'wide' :
35593                 this.x = 3;
35594                 this.y = 2;
35595                 break;
35596             case 'wide-thin' :
35597                 this.x = 3;
35598                 this.y = 1;
35599                 break;
35600                         
35601             default :
35602                 break;
35603         }
35604         
35605         if(Roo.isTouch){
35606             this.el.on('touchstart', this.onTouchStart, this);
35607             this.el.on('touchmove', this.onTouchMove, this);
35608             this.el.on('touchend', this.onTouchEnd, this);
35609             this.el.on('contextmenu', this.onContextMenu, this);
35610         } else {
35611             this.el.on('mouseenter'  ,this.enter, this);
35612             this.el.on('mouseleave', this.leave, this);
35613             this.el.on('click', this.onClick, this);
35614         }
35615         
35616         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35617             this.parent().bricks.push(this);   
35618         }
35619         
35620     },
35621     
35622     onClick: function(e, el)
35623     {
35624         var time = this.endTimer - this.startTimer;
35625         // Roo.log(e.preventDefault());
35626         if(Roo.isTouch){
35627             if(time > 1000){
35628                 e.preventDefault();
35629                 return;
35630             }
35631         }
35632         
35633         if(!this.preventDefault){
35634             return;
35635         }
35636         
35637         e.preventDefault();
35638         
35639         if (this.activeClass != '') {
35640             this.selectBrick();
35641         }
35642         
35643         this.fireEvent('click', this, e);
35644     },
35645     
35646     enter: function(e, el)
35647     {
35648         e.preventDefault();
35649         
35650         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35651             return;
35652         }
35653         
35654         if(this.bgimage.length && this.html.length){
35655             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35656         }
35657     },
35658     
35659     leave: function(e, el)
35660     {
35661         e.preventDefault();
35662         
35663         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35664             return;
35665         }
35666         
35667         if(this.bgimage.length && this.html.length){
35668             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35669         }
35670     },
35671     
35672     onTouchStart: function(e, el)
35673     {
35674 //        e.preventDefault();
35675         
35676         this.touchmoved = false;
35677         
35678         if(!this.isFitContainer){
35679             return;
35680         }
35681         
35682         if(!this.bgimage.length || !this.html.length){
35683             return;
35684         }
35685         
35686         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35687         
35688         this.timer = new Date().getTime();
35689         
35690     },
35691     
35692     onTouchMove: function(e, el)
35693     {
35694         this.touchmoved = true;
35695     },
35696     
35697     onContextMenu : function(e,el)
35698     {
35699         e.preventDefault();
35700         e.stopPropagation();
35701         return false;
35702     },
35703     
35704     onTouchEnd: function(e, el)
35705     {
35706 //        e.preventDefault();
35707         
35708         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35709         
35710             this.leave(e,el);
35711             
35712             return;
35713         }
35714         
35715         if(!this.bgimage.length || !this.html.length){
35716             
35717             if(this.href.length){
35718                 window.location.href = this.href;
35719             }
35720             
35721             return;
35722         }
35723         
35724         if(!this.isFitContainer){
35725             return;
35726         }
35727         
35728         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35729         
35730         window.location.href = this.href;
35731     },
35732     
35733     //selection on single brick only
35734     selectBrick : function() {
35735         
35736         if (!this.parentId) {
35737             return;
35738         }
35739         
35740         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35741         var index = m.selectedBrick.indexOf(this.id);
35742         
35743         if ( index > -1) {
35744             m.selectedBrick.splice(index,1);
35745             this.el.removeClass(this.activeClass);
35746             return;
35747         }
35748         
35749         for(var i = 0; i < m.selectedBrick.length; i++) {
35750             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35751             b.el.removeClass(b.activeClass);
35752         }
35753         
35754         m.selectedBrick = [];
35755         
35756         m.selectedBrick.push(this.id);
35757         this.el.addClass(this.activeClass);
35758         return;
35759     },
35760     
35761     isSelected : function(){
35762         return this.el.hasClass(this.activeClass);
35763         
35764     }
35765 });
35766
35767 Roo.apply(Roo.bootstrap.MasonryBrick, {
35768     
35769     //groups: {},
35770     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35771      /**
35772     * register a Masonry Brick
35773     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35774     */
35775     
35776     register : function(brick)
35777     {
35778         //this.groups[brick.id] = brick;
35779         this.groups.add(brick.id, brick);
35780     },
35781     /**
35782     * fetch a  masonry brick based on the masonry brick ID
35783     * @param {string} the masonry brick to add
35784     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35785     */
35786     
35787     get: function(brick_id) 
35788     {
35789         // if (typeof(this.groups[brick_id]) == 'undefined') {
35790         //     return false;
35791         // }
35792         // return this.groups[brick_id] ;
35793         
35794         if(this.groups.key(brick_id)) {
35795             return this.groups.key(brick_id);
35796         }
35797         
35798         return false;
35799     }
35800     
35801     
35802     
35803 });
35804
35805  /*
35806  * - LGPL
35807  *
35808  * element
35809  * 
35810  */
35811
35812 /**
35813  * @class Roo.bootstrap.Brick
35814  * @extends Roo.bootstrap.Component
35815  * Bootstrap Brick class
35816  * 
35817  * @constructor
35818  * Create a new Brick
35819  * @param {Object} config The config object
35820  */
35821
35822 Roo.bootstrap.Brick = function(config){
35823     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35824     
35825     this.addEvents({
35826         // raw events
35827         /**
35828          * @event click
35829          * When a Brick is click
35830          * @param {Roo.bootstrap.Brick} this
35831          * @param {Roo.EventObject} e
35832          */
35833         "click" : true
35834     });
35835 };
35836
35837 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35838     
35839     /**
35840      * @cfg {String} title
35841      */   
35842     title : '',
35843     /**
35844      * @cfg {String} html
35845      */   
35846     html : '',
35847     /**
35848      * @cfg {String} bgimage
35849      */   
35850     bgimage : '',
35851     /**
35852      * @cfg {String} cls
35853      */   
35854     cls : '',
35855     /**
35856      * @cfg {String} href
35857      */   
35858     href : '',
35859     /**
35860      * @cfg {String} video
35861      */   
35862     video : '',
35863     /**
35864      * @cfg {Boolean} square
35865      */   
35866     square : true,
35867     
35868     getAutoCreate : function()
35869     {
35870         var cls = 'roo-brick';
35871         
35872         if(this.href.length){
35873             cls += ' roo-brick-link';
35874         }
35875         
35876         if(this.bgimage.length){
35877             cls += ' roo-brick-image';
35878         }
35879         
35880         if(!this.html.length && !this.bgimage.length){
35881             cls += ' roo-brick-center-title';
35882         }
35883         
35884         if(!this.html.length && this.bgimage.length){
35885             cls += ' roo-brick-bottom-title';
35886         }
35887         
35888         if(this.cls){
35889             cls += ' ' + this.cls;
35890         }
35891         
35892         var cfg = {
35893             tag: (this.href.length) ? 'a' : 'div',
35894             cls: cls,
35895             cn: [
35896                 {
35897                     tag: 'div',
35898                     cls: 'roo-brick-paragraph',
35899                     cn: []
35900                 }
35901             ]
35902         };
35903         
35904         if(this.href.length){
35905             cfg.href = this.href;
35906         }
35907         
35908         var cn = cfg.cn[0].cn;
35909         
35910         if(this.title.length){
35911             cn.push({
35912                 tag: 'h4',
35913                 cls: 'roo-brick-title',
35914                 html: this.title
35915             });
35916         }
35917         
35918         if(this.html.length){
35919             cn.push({
35920                 tag: 'p',
35921                 cls: 'roo-brick-text',
35922                 html: this.html
35923             });
35924         } else {
35925             cn.cls += ' hide';
35926         }
35927         
35928         if(this.bgimage.length){
35929             cfg.cn.push({
35930                 tag: 'img',
35931                 cls: 'roo-brick-image-view',
35932                 src: this.bgimage
35933             });
35934         }
35935         
35936         return cfg;
35937     },
35938     
35939     initEvents: function() 
35940     {
35941         if(this.title.length || this.html.length){
35942             this.el.on('mouseenter'  ,this.enter, this);
35943             this.el.on('mouseleave', this.leave, this);
35944         }
35945         
35946         Roo.EventManager.onWindowResize(this.resize, this); 
35947         
35948         if(this.bgimage.length){
35949             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35950             this.imageEl.on('load', this.onImageLoad, this);
35951             return;
35952         }
35953         
35954         this.resize();
35955     },
35956     
35957     onImageLoad : function()
35958     {
35959         this.resize();
35960     },
35961     
35962     resize : function()
35963     {
35964         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35965         
35966         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35967         
35968         if(this.bgimage.length){
35969             var image = this.el.select('.roo-brick-image-view', true).first();
35970             
35971             image.setWidth(paragraph.getWidth());
35972             
35973             if(this.square){
35974                 image.setHeight(paragraph.getWidth());
35975             }
35976             
35977             this.el.setHeight(image.getHeight());
35978             paragraph.setHeight(image.getHeight());
35979             
35980         }
35981         
35982     },
35983     
35984     enter: function(e, el)
35985     {
35986         e.preventDefault();
35987         
35988         if(this.bgimage.length){
35989             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35990             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35991         }
35992     },
35993     
35994     leave: function(e, el)
35995     {
35996         e.preventDefault();
35997         
35998         if(this.bgimage.length){
35999             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36000             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36001         }
36002     }
36003     
36004 });
36005
36006  
36007
36008  /*
36009  * - LGPL
36010  *
36011  * Number field 
36012  */
36013
36014 /**
36015  * @class Roo.bootstrap.NumberField
36016  * @extends Roo.bootstrap.Input
36017  * Bootstrap NumberField class
36018  * 
36019  * 
36020  * 
36021  * 
36022  * @constructor
36023  * Create a new NumberField
36024  * @param {Object} config The config object
36025  */
36026
36027 Roo.bootstrap.NumberField = function(config){
36028     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36029 };
36030
36031 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36032     
36033     /**
36034      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36035      */
36036     allowDecimals : true,
36037     /**
36038      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36039      */
36040     decimalSeparator : ".",
36041     /**
36042      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36043      */
36044     decimalPrecision : 2,
36045     /**
36046      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36047      */
36048     allowNegative : true,
36049     
36050     /**
36051      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36052      */
36053     allowZero: true,
36054     /**
36055      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36056      */
36057     minValue : Number.NEGATIVE_INFINITY,
36058     /**
36059      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36060      */
36061     maxValue : Number.MAX_VALUE,
36062     /**
36063      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36064      */
36065     minText : "The minimum value for this field is {0}",
36066     /**
36067      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36068      */
36069     maxText : "The maximum value for this field is {0}",
36070     /**
36071      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36072      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36073      */
36074     nanText : "{0} is not a valid number",
36075     /**
36076      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36077      */
36078     thousandsDelimiter : false,
36079     /**
36080      * @cfg {String} valueAlign alignment of value
36081      */
36082     valueAlign : "left",
36083
36084     getAutoCreate : function()
36085     {
36086         var hiddenInput = {
36087             tag: 'input',
36088             type: 'hidden',
36089             id: Roo.id(),
36090             cls: 'hidden-number-input'
36091         };
36092         
36093         if (this.name) {
36094             hiddenInput.name = this.name;
36095         }
36096         
36097         this.name = '';
36098         
36099         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36100         
36101         this.name = hiddenInput.name;
36102         
36103         if(cfg.cn.length > 0) {
36104             cfg.cn.push(hiddenInput);
36105         }
36106         
36107         return cfg;
36108     },
36109
36110     // private
36111     initEvents : function()
36112     {   
36113         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36114         
36115         var allowed = "0123456789";
36116         
36117         if(this.allowDecimals){
36118             allowed += this.decimalSeparator;
36119         }
36120         
36121         if(this.allowNegative){
36122             allowed += "-";
36123         }
36124         
36125         if(this.thousandsDelimiter) {
36126             allowed += ",";
36127         }
36128         
36129         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36130         
36131         var keyPress = function(e){
36132             
36133             var k = e.getKey();
36134             
36135             var c = e.getCharCode();
36136             
36137             if(
36138                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36139                     allowed.indexOf(String.fromCharCode(c)) === -1
36140             ){
36141                 e.stopEvent();
36142                 return;
36143             }
36144             
36145             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36146                 return;
36147             }
36148             
36149             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36150                 e.stopEvent();
36151             }
36152         };
36153         
36154         this.el.on("keypress", keyPress, this);
36155     },
36156     
36157     validateValue : function(value)
36158     {
36159         
36160         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36161             return false;
36162         }
36163         
36164         var num = this.parseValue(value);
36165         
36166         if(isNaN(num)){
36167             this.markInvalid(String.format(this.nanText, value));
36168             return false;
36169         }
36170         
36171         if(num < this.minValue){
36172             this.markInvalid(String.format(this.minText, this.minValue));
36173             return false;
36174         }
36175         
36176         if(num > this.maxValue){
36177             this.markInvalid(String.format(this.maxText, this.maxValue));
36178             return false;
36179         }
36180         
36181         return true;
36182     },
36183
36184     getValue : function()
36185     {
36186         var v = this.hiddenEl().getValue();
36187         
36188         return this.fixPrecision(this.parseValue(v));
36189     },
36190
36191     parseValue : function(value)
36192     {
36193         if(this.thousandsDelimiter) {
36194             value += "";
36195             r = new RegExp(",", "g");
36196             value = value.replace(r, "");
36197         }
36198         
36199         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36200         return isNaN(value) ? '' : value;
36201     },
36202
36203     fixPrecision : function(value)
36204     {
36205         if(this.thousandsDelimiter) {
36206             value += "";
36207             r = new RegExp(",", "g");
36208             value = value.replace(r, "");
36209         }
36210         
36211         var nan = isNaN(value);
36212         
36213         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36214             return nan ? '' : value;
36215         }
36216         return parseFloat(value).toFixed(this.decimalPrecision);
36217     },
36218
36219     setValue : function(v)
36220     {
36221         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36222         
36223         this.value = v;
36224         
36225         if(this.rendered){
36226             
36227             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36228             
36229             this.inputEl().dom.value = (v == '') ? '' :
36230                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36231             
36232             if(!this.allowZero && v === '0') {
36233                 this.hiddenEl().dom.value = '';
36234                 this.inputEl().dom.value = '';
36235             }
36236             
36237             this.validate();
36238         }
36239     },
36240
36241     decimalPrecisionFcn : function(v)
36242     {
36243         return Math.floor(v);
36244     },
36245
36246     beforeBlur : function()
36247     {
36248         var v = this.parseValue(this.getRawValue());
36249         
36250         if(v || v === 0 || v === ''){
36251             this.setValue(v);
36252         }
36253     },
36254     
36255     hiddenEl : function()
36256     {
36257         return this.el.select('input.hidden-number-input',true).first();
36258     }
36259     
36260 });
36261
36262  
36263
36264 /*
36265 * Licence: LGPL
36266 */
36267
36268 /**
36269  * @class Roo.bootstrap.DocumentSlider
36270  * @extends Roo.bootstrap.Component
36271  * Bootstrap DocumentSlider class
36272  * 
36273  * @constructor
36274  * Create a new DocumentViewer
36275  * @param {Object} config The config object
36276  */
36277
36278 Roo.bootstrap.DocumentSlider = function(config){
36279     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36280     
36281     this.files = [];
36282     
36283     this.addEvents({
36284         /**
36285          * @event initial
36286          * Fire after initEvent
36287          * @param {Roo.bootstrap.DocumentSlider} this
36288          */
36289         "initial" : true,
36290         /**
36291          * @event update
36292          * Fire after update
36293          * @param {Roo.bootstrap.DocumentSlider} this
36294          */
36295         "update" : true,
36296         /**
36297          * @event click
36298          * Fire after click
36299          * @param {Roo.bootstrap.DocumentSlider} this
36300          */
36301         "click" : true
36302     });
36303 };
36304
36305 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36306     
36307     files : false,
36308     
36309     indicator : 0,
36310     
36311     getAutoCreate : function()
36312     {
36313         var cfg = {
36314             tag : 'div',
36315             cls : 'roo-document-slider',
36316             cn : [
36317                 {
36318                     tag : 'div',
36319                     cls : 'roo-document-slider-header',
36320                     cn : [
36321                         {
36322                             tag : 'div',
36323                             cls : 'roo-document-slider-header-title'
36324                         }
36325                     ]
36326                 },
36327                 {
36328                     tag : 'div',
36329                     cls : 'roo-document-slider-body',
36330                     cn : [
36331                         {
36332                             tag : 'div',
36333                             cls : 'roo-document-slider-prev',
36334                             cn : [
36335                                 {
36336                                     tag : 'i',
36337                                     cls : 'fa fa-chevron-left'
36338                                 }
36339                             ]
36340                         },
36341                         {
36342                             tag : 'div',
36343                             cls : 'roo-document-slider-thumb',
36344                             cn : [
36345                                 {
36346                                     tag : 'img',
36347                                     cls : 'roo-document-slider-image'
36348                                 }
36349                             ]
36350                         },
36351                         {
36352                             tag : 'div',
36353                             cls : 'roo-document-slider-next',
36354                             cn : [
36355                                 {
36356                                     tag : 'i',
36357                                     cls : 'fa fa-chevron-right'
36358                                 }
36359                             ]
36360                         }
36361                     ]
36362                 }
36363             ]
36364         };
36365         
36366         return cfg;
36367     },
36368     
36369     initEvents : function()
36370     {
36371         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36372         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36373         
36374         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36375         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36376         
36377         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36378         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36379         
36380         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36381         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36382         
36383         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36384         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36385         
36386         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36387         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36388         
36389         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36390         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36391         
36392         this.thumbEl.on('click', this.onClick, this);
36393         
36394         this.prevIndicator.on('click', this.prev, this);
36395         
36396         this.nextIndicator.on('click', this.next, this);
36397         
36398     },
36399     
36400     initial : function()
36401     {
36402         if(this.files.length){
36403             this.indicator = 1;
36404             this.update()
36405         }
36406         
36407         this.fireEvent('initial', this);
36408     },
36409     
36410     update : function()
36411     {
36412         this.imageEl.attr('src', this.files[this.indicator - 1]);
36413         
36414         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36415         
36416         this.prevIndicator.show();
36417         
36418         if(this.indicator == 1){
36419             this.prevIndicator.hide();
36420         }
36421         
36422         this.nextIndicator.show();
36423         
36424         if(this.indicator == this.files.length){
36425             this.nextIndicator.hide();
36426         }
36427         
36428         this.thumbEl.scrollTo('top');
36429         
36430         this.fireEvent('update', this);
36431     },
36432     
36433     onClick : function(e)
36434     {
36435         e.preventDefault();
36436         
36437         this.fireEvent('click', this);
36438     },
36439     
36440     prev : function(e)
36441     {
36442         e.preventDefault();
36443         
36444         this.indicator = Math.max(1, this.indicator - 1);
36445         
36446         this.update();
36447     },
36448     
36449     next : function(e)
36450     {
36451         e.preventDefault();
36452         
36453         this.indicator = Math.min(this.files.length, this.indicator + 1);
36454         
36455         this.update();
36456     }
36457 });
36458 /*
36459  * - LGPL
36460  *
36461  * RadioSet
36462  *
36463  *
36464  */
36465
36466 /**
36467  * @class Roo.bootstrap.RadioSet
36468  * @extends Roo.bootstrap.Input
36469  * Bootstrap RadioSet class
36470  * @cfg {String} indicatorpos (left|right) default left
36471  * @cfg {Boolean} inline (true|false) inline the element (default true)
36472  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36473  * @constructor
36474  * Create a new RadioSet
36475  * @param {Object} config The config object
36476  */
36477
36478 Roo.bootstrap.RadioSet = function(config){
36479     
36480     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36481     
36482     this.radioes = [];
36483     
36484     Roo.bootstrap.RadioSet.register(this);
36485     
36486     this.addEvents({
36487         /**
36488         * @event check
36489         * Fires when the element is checked or unchecked.
36490         * @param {Roo.bootstrap.RadioSet} this This radio
36491         * @param {Roo.bootstrap.Radio} item The checked item
36492         */
36493        check : true,
36494        /**
36495         * @event click
36496         * Fires when the element is click.
36497         * @param {Roo.bootstrap.RadioSet} this This radio set
36498         * @param {Roo.bootstrap.Radio} item The checked item
36499         * @param {Roo.EventObject} e The event object
36500         */
36501        click : true
36502     });
36503     
36504 };
36505
36506 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36507
36508     radioes : false,
36509     
36510     inline : true,
36511     
36512     weight : '',
36513     
36514     indicatorpos : 'left',
36515     
36516     getAutoCreate : function()
36517     {
36518         var label = {
36519             tag : 'label',
36520             cls : 'roo-radio-set-label',
36521             cn : [
36522                 {
36523                     tag : 'span',
36524                     html : this.fieldLabel
36525                 }
36526             ]
36527         };
36528         if (Roo.bootstrap.version == 3) {
36529             
36530             
36531             if(this.indicatorpos == 'left'){
36532                 label.cn.unshift({
36533                     tag : 'i',
36534                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36535                     tooltip : 'This field is required'
36536                 });
36537             } else {
36538                 label.cn.push({
36539                     tag : 'i',
36540                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36541                     tooltip : 'This field is required'
36542                 });
36543             }
36544         }
36545         var items = {
36546             tag : 'div',
36547             cls : 'roo-radio-set-items'
36548         };
36549         
36550         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36551         
36552         if (align === 'left' && this.fieldLabel.length) {
36553             
36554             items = {
36555                 cls : "roo-radio-set-right", 
36556                 cn: [
36557                     items
36558                 ]
36559             };
36560             
36561             if(this.labelWidth > 12){
36562                 label.style = "width: " + this.labelWidth + 'px';
36563             }
36564             
36565             if(this.labelWidth < 13 && this.labelmd == 0){
36566                 this.labelmd = this.labelWidth;
36567             }
36568             
36569             if(this.labellg > 0){
36570                 label.cls += ' col-lg-' + this.labellg;
36571                 items.cls += ' col-lg-' + (12 - this.labellg);
36572             }
36573             
36574             if(this.labelmd > 0){
36575                 label.cls += ' col-md-' + this.labelmd;
36576                 items.cls += ' col-md-' + (12 - this.labelmd);
36577             }
36578             
36579             if(this.labelsm > 0){
36580                 label.cls += ' col-sm-' + this.labelsm;
36581                 items.cls += ' col-sm-' + (12 - this.labelsm);
36582             }
36583             
36584             if(this.labelxs > 0){
36585                 label.cls += ' col-xs-' + this.labelxs;
36586                 items.cls += ' col-xs-' + (12 - this.labelxs);
36587             }
36588         }
36589         
36590         var cfg = {
36591             tag : 'div',
36592             cls : 'roo-radio-set',
36593             cn : [
36594                 {
36595                     tag : 'input',
36596                     cls : 'roo-radio-set-input',
36597                     type : 'hidden',
36598                     name : this.name,
36599                     value : this.value ? this.value :  ''
36600                 },
36601                 label,
36602                 items
36603             ]
36604         };
36605         
36606         if(this.weight.length){
36607             cfg.cls += ' roo-radio-' + this.weight;
36608         }
36609         
36610         if(this.inline) {
36611             cfg.cls += ' roo-radio-set-inline';
36612         }
36613         
36614         var settings=this;
36615         ['xs','sm','md','lg'].map(function(size){
36616             if (settings[size]) {
36617                 cfg.cls += ' col-' + size + '-' + settings[size];
36618             }
36619         });
36620         
36621         return cfg;
36622         
36623     },
36624
36625     initEvents : function()
36626     {
36627         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36628         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36629         
36630         if(!this.fieldLabel.length){
36631             this.labelEl.hide();
36632         }
36633         
36634         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36635         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36636         
36637         this.indicator = this.indicatorEl();
36638         
36639         if(this.indicator){
36640             this.indicator.addClass('invisible');
36641         }
36642         
36643         this.originalValue = this.getValue();
36644         
36645     },
36646     
36647     inputEl: function ()
36648     {
36649         return this.el.select('.roo-radio-set-input', true).first();
36650     },
36651     
36652     getChildContainer : function()
36653     {
36654         return this.itemsEl;
36655     },
36656     
36657     register : function(item)
36658     {
36659         this.radioes.push(item);
36660         
36661     },
36662     
36663     validate : function()
36664     {   
36665         if(this.getVisibilityEl().hasClass('hidden')){
36666             return true;
36667         }
36668         
36669         var valid = false;
36670         
36671         Roo.each(this.radioes, function(i){
36672             if(!i.checked){
36673                 return;
36674             }
36675             
36676             valid = true;
36677             return false;
36678         });
36679         
36680         if(this.allowBlank) {
36681             return true;
36682         }
36683         
36684         if(this.disabled || valid){
36685             this.markValid();
36686             return true;
36687         }
36688         
36689         this.markInvalid();
36690         return false;
36691         
36692     },
36693     
36694     markValid : function()
36695     {
36696         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36697             this.indicatorEl().removeClass('visible');
36698             this.indicatorEl().addClass('invisible');
36699         }
36700         
36701         
36702         if (Roo.bootstrap.version == 3) {
36703             this.el.removeClass([this.invalidClass, this.validClass]);
36704             this.el.addClass(this.validClass);
36705         } else {
36706             this.el.removeClass(['is-invalid','is-valid']);
36707             this.el.addClass(['is-valid']);
36708         }
36709         this.fireEvent('valid', this);
36710     },
36711     
36712     markInvalid : function(msg)
36713     {
36714         if(this.allowBlank || this.disabled){
36715             return;
36716         }
36717         
36718         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36719             this.indicatorEl().removeClass('invisible');
36720             this.indicatorEl().addClass('visible');
36721         }
36722         if (Roo.bootstrap.version == 3) {
36723             this.el.removeClass([this.invalidClass, this.validClass]);
36724             this.el.addClass(this.invalidClass);
36725         } else {
36726             this.el.removeClass(['is-invalid','is-valid']);
36727             this.el.addClass(['is-invalid']);
36728         }
36729         
36730         this.fireEvent('invalid', this, msg);
36731         
36732     },
36733     
36734     setValue : function(v, suppressEvent)
36735     {   
36736         if(this.value === v){
36737             return;
36738         }
36739         
36740         this.value = v;
36741         
36742         if(this.rendered){
36743             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36744         }
36745         
36746         Roo.each(this.radioes, function(i){
36747             i.checked = false;
36748             i.el.removeClass('checked');
36749         });
36750         
36751         Roo.each(this.radioes, function(i){
36752             
36753             if(i.value === v || i.value.toString() === v.toString()){
36754                 i.checked = true;
36755                 i.el.addClass('checked');
36756                 
36757                 if(suppressEvent !== true){
36758                     this.fireEvent('check', this, i);
36759                 }
36760                 
36761                 return false;
36762             }
36763             
36764         }, this);
36765         
36766         this.validate();
36767     },
36768     
36769     clearInvalid : function(){
36770         
36771         if(!this.el || this.preventMark){
36772             return;
36773         }
36774         
36775         this.el.removeClass([this.invalidClass]);
36776         
36777         this.fireEvent('valid', this);
36778     }
36779     
36780 });
36781
36782 Roo.apply(Roo.bootstrap.RadioSet, {
36783     
36784     groups: {},
36785     
36786     register : function(set)
36787     {
36788         this.groups[set.name] = set;
36789     },
36790     
36791     get: function(name) 
36792     {
36793         if (typeof(this.groups[name]) == 'undefined') {
36794             return false;
36795         }
36796         
36797         return this.groups[name] ;
36798     }
36799     
36800 });
36801 /*
36802  * Based on:
36803  * Ext JS Library 1.1.1
36804  * Copyright(c) 2006-2007, Ext JS, LLC.
36805  *
36806  * Originally Released Under LGPL - original licence link has changed is not relivant.
36807  *
36808  * Fork - LGPL
36809  * <script type="text/javascript">
36810  */
36811
36812
36813 /**
36814  * @class Roo.bootstrap.SplitBar
36815  * @extends Roo.util.Observable
36816  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36817  * <br><br>
36818  * Usage:
36819  * <pre><code>
36820 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36821                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36822 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36823 split.minSize = 100;
36824 split.maxSize = 600;
36825 split.animate = true;
36826 split.on('moved', splitterMoved);
36827 </code></pre>
36828  * @constructor
36829  * Create a new SplitBar
36830  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36831  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36832  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36833  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36834                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36835                         position of the SplitBar).
36836  */
36837 Roo.bootstrap.SplitBar = function(cfg){
36838     
36839     /** @private */
36840     
36841     //{
36842     //  dragElement : elm
36843     //  resizingElement: el,
36844         // optional..
36845     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36846     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36847         // existingProxy ???
36848     //}
36849     
36850     this.el = Roo.get(cfg.dragElement, true);
36851     this.el.dom.unselectable = "on";
36852     /** @private */
36853     this.resizingEl = Roo.get(cfg.resizingElement, true);
36854
36855     /**
36856      * @private
36857      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36858      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36859      * @type Number
36860      */
36861     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36862     
36863     /**
36864      * The minimum size of the resizing element. (Defaults to 0)
36865      * @type Number
36866      */
36867     this.minSize = 0;
36868     
36869     /**
36870      * The maximum size of the resizing element. (Defaults to 2000)
36871      * @type Number
36872      */
36873     this.maxSize = 2000;
36874     
36875     /**
36876      * Whether to animate the transition to the new size
36877      * @type Boolean
36878      */
36879     this.animate = false;
36880     
36881     /**
36882      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36883      * @type Boolean
36884      */
36885     this.useShim = false;
36886     
36887     /** @private */
36888     this.shim = null;
36889     
36890     if(!cfg.existingProxy){
36891         /** @private */
36892         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36893     }else{
36894         this.proxy = Roo.get(cfg.existingProxy).dom;
36895     }
36896     /** @private */
36897     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36898     
36899     /** @private */
36900     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36901     
36902     /** @private */
36903     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36904     
36905     /** @private */
36906     this.dragSpecs = {};
36907     
36908     /**
36909      * @private The adapter to use to positon and resize elements
36910      */
36911     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36912     this.adapter.init(this);
36913     
36914     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36915         /** @private */
36916         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36917         this.el.addClass("roo-splitbar-h");
36918     }else{
36919         /** @private */
36920         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36921         this.el.addClass("roo-splitbar-v");
36922     }
36923     
36924     this.addEvents({
36925         /**
36926          * @event resize
36927          * Fires when the splitter is moved (alias for {@link #event-moved})
36928          * @param {Roo.bootstrap.SplitBar} this
36929          * @param {Number} newSize the new width or height
36930          */
36931         "resize" : true,
36932         /**
36933          * @event moved
36934          * Fires when the splitter is moved
36935          * @param {Roo.bootstrap.SplitBar} this
36936          * @param {Number} newSize the new width or height
36937          */
36938         "moved" : true,
36939         /**
36940          * @event beforeresize
36941          * Fires before the splitter is dragged
36942          * @param {Roo.bootstrap.SplitBar} this
36943          */
36944         "beforeresize" : true,
36945
36946         "beforeapply" : true
36947     });
36948
36949     Roo.util.Observable.call(this);
36950 };
36951
36952 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36953     onStartProxyDrag : function(x, y){
36954         this.fireEvent("beforeresize", this);
36955         if(!this.overlay){
36956             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36957             o.unselectable();
36958             o.enableDisplayMode("block");
36959             // all splitbars share the same overlay
36960             Roo.bootstrap.SplitBar.prototype.overlay = o;
36961         }
36962         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36963         this.overlay.show();
36964         Roo.get(this.proxy).setDisplayed("block");
36965         var size = this.adapter.getElementSize(this);
36966         this.activeMinSize = this.getMinimumSize();;
36967         this.activeMaxSize = this.getMaximumSize();;
36968         var c1 = size - this.activeMinSize;
36969         var c2 = Math.max(this.activeMaxSize - size, 0);
36970         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36971             this.dd.resetConstraints();
36972             this.dd.setXConstraint(
36973                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36974                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36975             );
36976             this.dd.setYConstraint(0, 0);
36977         }else{
36978             this.dd.resetConstraints();
36979             this.dd.setXConstraint(0, 0);
36980             this.dd.setYConstraint(
36981                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36982                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36983             );
36984          }
36985         this.dragSpecs.startSize = size;
36986         this.dragSpecs.startPoint = [x, y];
36987         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36988     },
36989     
36990     /** 
36991      * @private Called after the drag operation by the DDProxy
36992      */
36993     onEndProxyDrag : function(e){
36994         Roo.get(this.proxy).setDisplayed(false);
36995         var endPoint = Roo.lib.Event.getXY(e);
36996         if(this.overlay){
36997             this.overlay.hide();
36998         }
36999         var newSize;
37000         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37001             newSize = this.dragSpecs.startSize + 
37002                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37003                     endPoint[0] - this.dragSpecs.startPoint[0] :
37004                     this.dragSpecs.startPoint[0] - endPoint[0]
37005                 );
37006         }else{
37007             newSize = this.dragSpecs.startSize + 
37008                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37009                     endPoint[1] - this.dragSpecs.startPoint[1] :
37010                     this.dragSpecs.startPoint[1] - endPoint[1]
37011                 );
37012         }
37013         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37014         if(newSize != this.dragSpecs.startSize){
37015             if(this.fireEvent('beforeapply', this, newSize) !== false){
37016                 this.adapter.setElementSize(this, newSize);
37017                 this.fireEvent("moved", this, newSize);
37018                 this.fireEvent("resize", this, newSize);
37019             }
37020         }
37021     },
37022     
37023     /**
37024      * Get the adapter this SplitBar uses
37025      * @return The adapter object
37026      */
37027     getAdapter : function(){
37028         return this.adapter;
37029     },
37030     
37031     /**
37032      * Set the adapter this SplitBar uses
37033      * @param {Object} adapter A SplitBar adapter object
37034      */
37035     setAdapter : function(adapter){
37036         this.adapter = adapter;
37037         this.adapter.init(this);
37038     },
37039     
37040     /**
37041      * Gets the minimum size for the resizing element
37042      * @return {Number} The minimum size
37043      */
37044     getMinimumSize : function(){
37045         return this.minSize;
37046     },
37047     
37048     /**
37049      * Sets the minimum size for the resizing element
37050      * @param {Number} minSize The minimum size
37051      */
37052     setMinimumSize : function(minSize){
37053         this.minSize = minSize;
37054     },
37055     
37056     /**
37057      * Gets the maximum size for the resizing element
37058      * @return {Number} The maximum size
37059      */
37060     getMaximumSize : function(){
37061         return this.maxSize;
37062     },
37063     
37064     /**
37065      * Sets the maximum size for the resizing element
37066      * @param {Number} maxSize The maximum size
37067      */
37068     setMaximumSize : function(maxSize){
37069         this.maxSize = maxSize;
37070     },
37071     
37072     /**
37073      * Sets the initialize size for the resizing element
37074      * @param {Number} size The initial size
37075      */
37076     setCurrentSize : function(size){
37077         var oldAnimate = this.animate;
37078         this.animate = false;
37079         this.adapter.setElementSize(this, size);
37080         this.animate = oldAnimate;
37081     },
37082     
37083     /**
37084      * Destroy this splitbar. 
37085      * @param {Boolean} removeEl True to remove the element
37086      */
37087     destroy : function(removeEl){
37088         if(this.shim){
37089             this.shim.remove();
37090         }
37091         this.dd.unreg();
37092         this.proxy.parentNode.removeChild(this.proxy);
37093         if(removeEl){
37094             this.el.remove();
37095         }
37096     }
37097 });
37098
37099 /**
37100  * @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.
37101  */
37102 Roo.bootstrap.SplitBar.createProxy = function(dir){
37103     var proxy = new Roo.Element(document.createElement("div"));
37104     proxy.unselectable();
37105     var cls = 'roo-splitbar-proxy';
37106     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37107     document.body.appendChild(proxy.dom);
37108     return proxy.dom;
37109 };
37110
37111 /** 
37112  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37113  * Default Adapter. It assumes the splitter and resizing element are not positioned
37114  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37115  */
37116 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37117 };
37118
37119 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37120     // do nothing for now
37121     init : function(s){
37122     
37123     },
37124     /**
37125      * Called before drag operations to get the current size of the resizing element. 
37126      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37127      */
37128      getElementSize : function(s){
37129         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37130             return s.resizingEl.getWidth();
37131         }else{
37132             return s.resizingEl.getHeight();
37133         }
37134     },
37135     
37136     /**
37137      * Called after drag operations to set the size of the resizing element.
37138      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37139      * @param {Number} newSize The new size to set
37140      * @param {Function} onComplete A function to be invoked when resizing is complete
37141      */
37142     setElementSize : function(s, newSize, onComplete){
37143         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37144             if(!s.animate){
37145                 s.resizingEl.setWidth(newSize);
37146                 if(onComplete){
37147                     onComplete(s, newSize);
37148                 }
37149             }else{
37150                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37151             }
37152         }else{
37153             
37154             if(!s.animate){
37155                 s.resizingEl.setHeight(newSize);
37156                 if(onComplete){
37157                     onComplete(s, newSize);
37158                 }
37159             }else{
37160                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37161             }
37162         }
37163     }
37164 };
37165
37166 /** 
37167  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37168  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37169  * Adapter that  moves the splitter element to align with the resized sizing element. 
37170  * Used with an absolute positioned SplitBar.
37171  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37172  * document.body, make sure you assign an id to the body element.
37173  */
37174 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37175     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37176     this.container = Roo.get(container);
37177 };
37178
37179 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37180     init : function(s){
37181         this.basic.init(s);
37182     },
37183     
37184     getElementSize : function(s){
37185         return this.basic.getElementSize(s);
37186     },
37187     
37188     setElementSize : function(s, newSize, onComplete){
37189         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37190     },
37191     
37192     moveSplitter : function(s){
37193         var yes = Roo.bootstrap.SplitBar;
37194         switch(s.placement){
37195             case yes.LEFT:
37196                 s.el.setX(s.resizingEl.getRight());
37197                 break;
37198             case yes.RIGHT:
37199                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37200                 break;
37201             case yes.TOP:
37202                 s.el.setY(s.resizingEl.getBottom());
37203                 break;
37204             case yes.BOTTOM:
37205                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37206                 break;
37207         }
37208     }
37209 };
37210
37211 /**
37212  * Orientation constant - Create a vertical SplitBar
37213  * @static
37214  * @type Number
37215  */
37216 Roo.bootstrap.SplitBar.VERTICAL = 1;
37217
37218 /**
37219  * Orientation constant - Create a horizontal SplitBar
37220  * @static
37221  * @type Number
37222  */
37223 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37224
37225 /**
37226  * Placement constant - The resizing element is to the left of the splitter element
37227  * @static
37228  * @type Number
37229  */
37230 Roo.bootstrap.SplitBar.LEFT = 1;
37231
37232 /**
37233  * Placement constant - The resizing element is to the right of the splitter element
37234  * @static
37235  * @type Number
37236  */
37237 Roo.bootstrap.SplitBar.RIGHT = 2;
37238
37239 /**
37240  * Placement constant - The resizing element is positioned above the splitter element
37241  * @static
37242  * @type Number
37243  */
37244 Roo.bootstrap.SplitBar.TOP = 3;
37245
37246 /**
37247  * Placement constant - The resizing element is positioned under splitter element
37248  * @static
37249  * @type Number
37250  */
37251 Roo.bootstrap.SplitBar.BOTTOM = 4;
37252 Roo.namespace("Roo.bootstrap.layout");/*
37253  * Based on:
37254  * Ext JS Library 1.1.1
37255  * Copyright(c) 2006-2007, Ext JS, LLC.
37256  *
37257  * Originally Released Under LGPL - original licence link has changed is not relivant.
37258  *
37259  * Fork - LGPL
37260  * <script type="text/javascript">
37261  */
37262
37263 /**
37264  * @class Roo.bootstrap.layout.Manager
37265  * @extends Roo.bootstrap.Component
37266  * Base class for layout managers.
37267  */
37268 Roo.bootstrap.layout.Manager = function(config)
37269 {
37270     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37271
37272
37273
37274
37275
37276     /** false to disable window resize monitoring @type Boolean */
37277     this.monitorWindowResize = true;
37278     this.regions = {};
37279     this.addEvents({
37280         /**
37281          * @event layout
37282          * Fires when a layout is performed.
37283          * @param {Roo.LayoutManager} this
37284          */
37285         "layout" : true,
37286         /**
37287          * @event regionresized
37288          * Fires when the user resizes a region.
37289          * @param {Roo.LayoutRegion} region The resized region
37290          * @param {Number} newSize The new size (width for east/west, height for north/south)
37291          */
37292         "regionresized" : true,
37293         /**
37294          * @event regioncollapsed
37295          * Fires when a region is collapsed.
37296          * @param {Roo.LayoutRegion} region The collapsed region
37297          */
37298         "regioncollapsed" : true,
37299         /**
37300          * @event regionexpanded
37301          * Fires when a region is expanded.
37302          * @param {Roo.LayoutRegion} region The expanded region
37303          */
37304         "regionexpanded" : true
37305     });
37306     this.updating = false;
37307
37308     if (config.el) {
37309         this.el = Roo.get(config.el);
37310         this.initEvents();
37311     }
37312
37313 };
37314
37315 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37316
37317
37318     regions : null,
37319
37320     monitorWindowResize : true,
37321
37322
37323     updating : false,
37324
37325
37326     onRender : function(ct, position)
37327     {
37328         if(!this.el){
37329             this.el = Roo.get(ct);
37330             this.initEvents();
37331         }
37332         //this.fireEvent('render',this);
37333     },
37334
37335
37336     initEvents: function()
37337     {
37338
37339
37340         // ie scrollbar fix
37341         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37342             document.body.scroll = "no";
37343         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37344             this.el.position('relative');
37345         }
37346         this.id = this.el.id;
37347         this.el.addClass("roo-layout-container");
37348         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37349         if(this.el.dom != document.body ) {
37350             this.el.on('resize', this.layout,this);
37351             this.el.on('show', this.layout,this);
37352         }
37353
37354     },
37355
37356     /**
37357      * Returns true if this layout is currently being updated
37358      * @return {Boolean}
37359      */
37360     isUpdating : function(){
37361         return this.updating;
37362     },
37363
37364     /**
37365      * Suspend the LayoutManager from doing auto-layouts while
37366      * making multiple add or remove calls
37367      */
37368     beginUpdate : function(){
37369         this.updating = true;
37370     },
37371
37372     /**
37373      * Restore auto-layouts and optionally disable the manager from performing a layout
37374      * @param {Boolean} noLayout true to disable a layout update
37375      */
37376     endUpdate : function(noLayout){
37377         this.updating = false;
37378         if(!noLayout){
37379             this.layout();
37380         }
37381     },
37382
37383     layout: function(){
37384         // abstract...
37385     },
37386
37387     onRegionResized : function(region, newSize){
37388         this.fireEvent("regionresized", region, newSize);
37389         this.layout();
37390     },
37391
37392     onRegionCollapsed : function(region){
37393         this.fireEvent("regioncollapsed", region);
37394     },
37395
37396     onRegionExpanded : function(region){
37397         this.fireEvent("regionexpanded", region);
37398     },
37399
37400     /**
37401      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37402      * performs box-model adjustments.
37403      * @return {Object} The size as an object {width: (the width), height: (the height)}
37404      */
37405     getViewSize : function()
37406     {
37407         var size;
37408         if(this.el.dom != document.body){
37409             size = this.el.getSize();
37410         }else{
37411             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37412         }
37413         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37414         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37415         return size;
37416     },
37417
37418     /**
37419      * Returns the Element this layout is bound to.
37420      * @return {Roo.Element}
37421      */
37422     getEl : function(){
37423         return this.el;
37424     },
37425
37426     /**
37427      * Returns the specified region.
37428      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37429      * @return {Roo.LayoutRegion}
37430      */
37431     getRegion : function(target){
37432         return this.regions[target.toLowerCase()];
37433     },
37434
37435     onWindowResize : function(){
37436         if(this.monitorWindowResize){
37437             this.layout();
37438         }
37439     }
37440 });
37441 /*
37442  * Based on:
37443  * Ext JS Library 1.1.1
37444  * Copyright(c) 2006-2007, Ext JS, LLC.
37445  *
37446  * Originally Released Under LGPL - original licence link has changed is not relivant.
37447  *
37448  * Fork - LGPL
37449  * <script type="text/javascript">
37450  */
37451 /**
37452  * @class Roo.bootstrap.layout.Border
37453  * @extends Roo.bootstrap.layout.Manager
37454  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37455  * please see: examples/bootstrap/nested.html<br><br>
37456  
37457 <b>The container the layout is rendered into can be either the body element or any other element.
37458 If it is not the body element, the container needs to either be an absolute positioned element,
37459 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37460 the container size if it is not the body element.</b>
37461
37462 * @constructor
37463 * Create a new Border
37464 * @param {Object} config Configuration options
37465  */
37466 Roo.bootstrap.layout.Border = function(config){
37467     config = config || {};
37468     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37469     
37470     
37471     
37472     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37473         if(config[region]){
37474             config[region].region = region;
37475             this.addRegion(config[region]);
37476         }
37477     },this);
37478     
37479 };
37480
37481 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37482
37483 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37484     
37485     parent : false, // this might point to a 'nest' or a ???
37486     
37487     /**
37488      * Creates and adds a new region if it doesn't already exist.
37489      * @param {String} target The target region key (north, south, east, west or center).
37490      * @param {Object} config The regions config object
37491      * @return {BorderLayoutRegion} The new region
37492      */
37493     addRegion : function(config)
37494     {
37495         if(!this.regions[config.region]){
37496             var r = this.factory(config);
37497             this.bindRegion(r);
37498         }
37499         return this.regions[config.region];
37500     },
37501
37502     // private (kinda)
37503     bindRegion : function(r){
37504         this.regions[r.config.region] = r;
37505         
37506         r.on("visibilitychange",    this.layout, this);
37507         r.on("paneladded",          this.layout, this);
37508         r.on("panelremoved",        this.layout, this);
37509         r.on("invalidated",         this.layout, this);
37510         r.on("resized",             this.onRegionResized, this);
37511         r.on("collapsed",           this.onRegionCollapsed, this);
37512         r.on("expanded",            this.onRegionExpanded, this);
37513     },
37514
37515     /**
37516      * Performs a layout update.
37517      */
37518     layout : function()
37519     {
37520         if(this.updating) {
37521             return;
37522         }
37523         
37524         // render all the rebions if they have not been done alreayd?
37525         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37526             if(this.regions[region] && !this.regions[region].bodyEl){
37527                 this.regions[region].onRender(this.el)
37528             }
37529         },this);
37530         
37531         var size = this.getViewSize();
37532         var w = size.width;
37533         var h = size.height;
37534         var centerW = w;
37535         var centerH = h;
37536         var centerY = 0;
37537         var centerX = 0;
37538         //var x = 0, y = 0;
37539
37540         var rs = this.regions;
37541         var north = rs["north"];
37542         var south = rs["south"]; 
37543         var west = rs["west"];
37544         var east = rs["east"];
37545         var center = rs["center"];
37546         //if(this.hideOnLayout){ // not supported anymore
37547             //c.el.setStyle("display", "none");
37548         //}
37549         if(north && north.isVisible()){
37550             var b = north.getBox();
37551             var m = north.getMargins();
37552             b.width = w - (m.left+m.right);
37553             b.x = m.left;
37554             b.y = m.top;
37555             centerY = b.height + b.y + m.bottom;
37556             centerH -= centerY;
37557             north.updateBox(this.safeBox(b));
37558         }
37559         if(south && south.isVisible()){
37560             var b = south.getBox();
37561             var m = south.getMargins();
37562             b.width = w - (m.left+m.right);
37563             b.x = m.left;
37564             var totalHeight = (b.height + m.top + m.bottom);
37565             b.y = h - totalHeight + m.top;
37566             centerH -= totalHeight;
37567             south.updateBox(this.safeBox(b));
37568         }
37569         if(west && west.isVisible()){
37570             var b = west.getBox();
37571             var m = west.getMargins();
37572             b.height = centerH - (m.top+m.bottom);
37573             b.x = m.left;
37574             b.y = centerY + m.top;
37575             var totalWidth = (b.width + m.left + m.right);
37576             centerX += totalWidth;
37577             centerW -= totalWidth;
37578             west.updateBox(this.safeBox(b));
37579         }
37580         if(east && east.isVisible()){
37581             var b = east.getBox();
37582             var m = east.getMargins();
37583             b.height = centerH - (m.top+m.bottom);
37584             var totalWidth = (b.width + m.left + m.right);
37585             b.x = w - totalWidth + m.left;
37586             b.y = centerY + m.top;
37587             centerW -= totalWidth;
37588             east.updateBox(this.safeBox(b));
37589         }
37590         if(center){
37591             var m = center.getMargins();
37592             var centerBox = {
37593                 x: centerX + m.left,
37594                 y: centerY + m.top,
37595                 width: centerW - (m.left+m.right),
37596                 height: centerH - (m.top+m.bottom)
37597             };
37598             //if(this.hideOnLayout){
37599                 //center.el.setStyle("display", "block");
37600             //}
37601             center.updateBox(this.safeBox(centerBox));
37602         }
37603         this.el.repaint();
37604         this.fireEvent("layout", this);
37605     },
37606
37607     // private
37608     safeBox : function(box){
37609         box.width = Math.max(0, box.width);
37610         box.height = Math.max(0, box.height);
37611         return box;
37612     },
37613
37614     /**
37615      * Adds a ContentPanel (or subclass) to this layout.
37616      * @param {String} target The target region key (north, south, east, west or center).
37617      * @param {Roo.ContentPanel} panel The panel to add
37618      * @return {Roo.ContentPanel} The added panel
37619      */
37620     add : function(target, panel){
37621          
37622         target = target.toLowerCase();
37623         return this.regions[target].add(panel);
37624     },
37625
37626     /**
37627      * Remove a ContentPanel (or subclass) to this layout.
37628      * @param {String} target The target region key (north, south, east, west or center).
37629      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37630      * @return {Roo.ContentPanel} The removed panel
37631      */
37632     remove : function(target, panel){
37633         target = target.toLowerCase();
37634         return this.regions[target].remove(panel);
37635     },
37636
37637     /**
37638      * Searches all regions for a panel with the specified id
37639      * @param {String} panelId
37640      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37641      */
37642     findPanel : function(panelId){
37643         var rs = this.regions;
37644         for(var target in rs){
37645             if(typeof rs[target] != "function"){
37646                 var p = rs[target].getPanel(panelId);
37647                 if(p){
37648                     return p;
37649                 }
37650             }
37651         }
37652         return null;
37653     },
37654
37655     /**
37656      * Searches all regions for a panel with the specified id and activates (shows) it.
37657      * @param {String/ContentPanel} panelId The panels id or the panel itself
37658      * @return {Roo.ContentPanel} The shown panel or null
37659      */
37660     showPanel : function(panelId) {
37661       var rs = this.regions;
37662       for(var target in rs){
37663          var r = rs[target];
37664          if(typeof r != "function"){
37665             if(r.hasPanel(panelId)){
37666                return r.showPanel(panelId);
37667             }
37668          }
37669       }
37670       return null;
37671    },
37672
37673    /**
37674      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37675      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37676      */
37677    /*
37678     restoreState : function(provider){
37679         if(!provider){
37680             provider = Roo.state.Manager;
37681         }
37682         var sm = new Roo.LayoutStateManager();
37683         sm.init(this, provider);
37684     },
37685 */
37686  
37687  
37688     /**
37689      * Adds a xtype elements to the layout.
37690      * <pre><code>
37691
37692 layout.addxtype({
37693        xtype : 'ContentPanel',
37694        region: 'west',
37695        items: [ .... ]
37696    }
37697 );
37698
37699 layout.addxtype({
37700         xtype : 'NestedLayoutPanel',
37701         region: 'west',
37702         layout: {
37703            center: { },
37704            west: { }   
37705         },
37706         items : [ ... list of content panels or nested layout panels.. ]
37707    }
37708 );
37709 </code></pre>
37710      * @param {Object} cfg Xtype definition of item to add.
37711      */
37712     addxtype : function(cfg)
37713     {
37714         // basically accepts a pannel...
37715         // can accept a layout region..!?!?
37716         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37717         
37718         
37719         // theory?  children can only be panels??
37720         
37721         //if (!cfg.xtype.match(/Panel$/)) {
37722         //    return false;
37723         //}
37724         var ret = false;
37725         
37726         if (typeof(cfg.region) == 'undefined') {
37727             Roo.log("Failed to add Panel, region was not set");
37728             Roo.log(cfg);
37729             return false;
37730         }
37731         var region = cfg.region;
37732         delete cfg.region;
37733         
37734           
37735         var xitems = [];
37736         if (cfg.items) {
37737             xitems = cfg.items;
37738             delete cfg.items;
37739         }
37740         var nb = false;
37741         
37742         if ( region == 'center') {
37743             Roo.log("Center: " + cfg.title);
37744         }
37745         
37746         
37747         switch(cfg.xtype) 
37748         {
37749             case 'Content':  // ContentPanel (el, cfg)
37750             case 'Scroll':  // ContentPanel (el, cfg)
37751             case 'View': 
37752                 cfg.autoCreate = cfg.autoCreate || true;
37753                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37754                 //} else {
37755                 //    var el = this.el.createChild();
37756                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37757                 //}
37758                 
37759                 this.add(region, ret);
37760                 break;
37761             
37762             /*
37763             case 'TreePanel': // our new panel!
37764                 cfg.el = this.el.createChild();
37765                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37766                 this.add(region, ret);
37767                 break;
37768             */
37769             
37770             case 'Nest': 
37771                 // create a new Layout (which is  a Border Layout...
37772                 
37773                 var clayout = cfg.layout;
37774                 clayout.el  = this.el.createChild();
37775                 clayout.items   = clayout.items  || [];
37776                 
37777                 delete cfg.layout;
37778                 
37779                 // replace this exitems with the clayout ones..
37780                 xitems = clayout.items;
37781                  
37782                 // force background off if it's in center...
37783                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37784                     cfg.background = false;
37785                 }
37786                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37787                 
37788                 
37789                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37790                 //console.log('adding nested layout panel '  + cfg.toSource());
37791                 this.add(region, ret);
37792                 nb = {}; /// find first...
37793                 break;
37794             
37795             case 'Grid':
37796                 
37797                 // needs grid and region
37798                 
37799                 //var el = this.getRegion(region).el.createChild();
37800                 /*
37801                  *var el = this.el.createChild();
37802                 // create the grid first...
37803                 cfg.grid.container = el;
37804                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37805                 */
37806                 
37807                 if (region == 'center' && this.active ) {
37808                     cfg.background = false;
37809                 }
37810                 
37811                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37812                 
37813                 this.add(region, ret);
37814                 /*
37815                 if (cfg.background) {
37816                     // render grid on panel activation (if panel background)
37817                     ret.on('activate', function(gp) {
37818                         if (!gp.grid.rendered) {
37819                     //        gp.grid.render(el);
37820                         }
37821                     });
37822                 } else {
37823                   //  cfg.grid.render(el);
37824                 }
37825                 */
37826                 break;
37827            
37828            
37829             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37830                 // it was the old xcomponent building that caused this before.
37831                 // espeically if border is the top element in the tree.
37832                 ret = this;
37833                 break; 
37834                 
37835                     
37836                 
37837                 
37838                 
37839             default:
37840                 /*
37841                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37842                     
37843                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37844                     this.add(region, ret);
37845                 } else {
37846                 */
37847                     Roo.log(cfg);
37848                     throw "Can not add '" + cfg.xtype + "' to Border";
37849                     return null;
37850              
37851                                 
37852              
37853         }
37854         this.beginUpdate();
37855         // add children..
37856         var region = '';
37857         var abn = {};
37858         Roo.each(xitems, function(i)  {
37859             region = nb && i.region ? i.region : false;
37860             
37861             var add = ret.addxtype(i);
37862            
37863             if (region) {
37864                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37865                 if (!i.background) {
37866                     abn[region] = nb[region] ;
37867                 }
37868             }
37869             
37870         });
37871         this.endUpdate();
37872
37873         // make the last non-background panel active..
37874         //if (nb) { Roo.log(abn); }
37875         if (nb) {
37876             
37877             for(var r in abn) {
37878                 region = this.getRegion(r);
37879                 if (region) {
37880                     // tried using nb[r], but it does not work..
37881                      
37882                     region.showPanel(abn[r]);
37883                    
37884                 }
37885             }
37886         }
37887         return ret;
37888         
37889     },
37890     
37891     
37892 // private
37893     factory : function(cfg)
37894     {
37895         
37896         var validRegions = Roo.bootstrap.layout.Border.regions;
37897
37898         var target = cfg.region;
37899         cfg.mgr = this;
37900         
37901         var r = Roo.bootstrap.layout;
37902         Roo.log(target);
37903         switch(target){
37904             case "north":
37905                 return new r.North(cfg);
37906             case "south":
37907                 return new r.South(cfg);
37908             case "east":
37909                 return new r.East(cfg);
37910             case "west":
37911                 return new r.West(cfg);
37912             case "center":
37913                 return new r.Center(cfg);
37914         }
37915         throw 'Layout region "'+target+'" not supported.';
37916     }
37917     
37918     
37919 });
37920  /*
37921  * Based on:
37922  * Ext JS Library 1.1.1
37923  * Copyright(c) 2006-2007, Ext JS, LLC.
37924  *
37925  * Originally Released Under LGPL - original licence link has changed is not relivant.
37926  *
37927  * Fork - LGPL
37928  * <script type="text/javascript">
37929  */
37930  
37931 /**
37932  * @class Roo.bootstrap.layout.Basic
37933  * @extends Roo.util.Observable
37934  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37935  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37936  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37937  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37938  * @cfg {string}   region  the region that it inhabits..
37939  * @cfg {bool}   skipConfig skip config?
37940  * 
37941
37942  */
37943 Roo.bootstrap.layout.Basic = function(config){
37944     
37945     this.mgr = config.mgr;
37946     
37947     this.position = config.region;
37948     
37949     var skipConfig = config.skipConfig;
37950     
37951     this.events = {
37952         /**
37953          * @scope Roo.BasicLayoutRegion
37954          */
37955         
37956         /**
37957          * @event beforeremove
37958          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37959          * @param {Roo.LayoutRegion} this
37960          * @param {Roo.ContentPanel} panel The panel
37961          * @param {Object} e The cancel event object
37962          */
37963         "beforeremove" : true,
37964         /**
37965          * @event invalidated
37966          * Fires when the layout for this region is changed.
37967          * @param {Roo.LayoutRegion} this
37968          */
37969         "invalidated" : true,
37970         /**
37971          * @event visibilitychange
37972          * Fires when this region is shown or hidden 
37973          * @param {Roo.LayoutRegion} this
37974          * @param {Boolean} visibility true or false
37975          */
37976         "visibilitychange" : true,
37977         /**
37978          * @event paneladded
37979          * Fires when a panel is added. 
37980          * @param {Roo.LayoutRegion} this
37981          * @param {Roo.ContentPanel} panel The panel
37982          */
37983         "paneladded" : true,
37984         /**
37985          * @event panelremoved
37986          * Fires when a panel is removed. 
37987          * @param {Roo.LayoutRegion} this
37988          * @param {Roo.ContentPanel} panel The panel
37989          */
37990         "panelremoved" : true,
37991         /**
37992          * @event beforecollapse
37993          * Fires when this region before collapse.
37994          * @param {Roo.LayoutRegion} this
37995          */
37996         "beforecollapse" : true,
37997         /**
37998          * @event collapsed
37999          * Fires when this region is collapsed.
38000          * @param {Roo.LayoutRegion} this
38001          */
38002         "collapsed" : true,
38003         /**
38004          * @event expanded
38005          * Fires when this region is expanded.
38006          * @param {Roo.LayoutRegion} this
38007          */
38008         "expanded" : true,
38009         /**
38010          * @event slideshow
38011          * Fires when this region is slid into view.
38012          * @param {Roo.LayoutRegion} this
38013          */
38014         "slideshow" : true,
38015         /**
38016          * @event slidehide
38017          * Fires when this region slides out of view. 
38018          * @param {Roo.LayoutRegion} this
38019          */
38020         "slidehide" : true,
38021         /**
38022          * @event panelactivated
38023          * Fires when a panel is activated. 
38024          * @param {Roo.LayoutRegion} this
38025          * @param {Roo.ContentPanel} panel The activated panel
38026          */
38027         "panelactivated" : true,
38028         /**
38029          * @event resized
38030          * Fires when the user resizes this region. 
38031          * @param {Roo.LayoutRegion} this
38032          * @param {Number} newSize The new size (width for east/west, height for north/south)
38033          */
38034         "resized" : true
38035     };
38036     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38037     this.panels = new Roo.util.MixedCollection();
38038     this.panels.getKey = this.getPanelId.createDelegate(this);
38039     this.box = null;
38040     this.activePanel = null;
38041     // ensure listeners are added...
38042     
38043     if (config.listeners || config.events) {
38044         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38045             listeners : config.listeners || {},
38046             events : config.events || {}
38047         });
38048     }
38049     
38050     if(skipConfig !== true){
38051         this.applyConfig(config);
38052     }
38053 };
38054
38055 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38056 {
38057     getPanelId : function(p){
38058         return p.getId();
38059     },
38060     
38061     applyConfig : function(config){
38062         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38063         this.config = config;
38064         
38065     },
38066     
38067     /**
38068      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38069      * the width, for horizontal (north, south) the height.
38070      * @param {Number} newSize The new width or height
38071      */
38072     resizeTo : function(newSize){
38073         var el = this.el ? this.el :
38074                  (this.activePanel ? this.activePanel.getEl() : null);
38075         if(el){
38076             switch(this.position){
38077                 case "east":
38078                 case "west":
38079                     el.setWidth(newSize);
38080                     this.fireEvent("resized", this, newSize);
38081                 break;
38082                 case "north":
38083                 case "south":
38084                     el.setHeight(newSize);
38085                     this.fireEvent("resized", this, newSize);
38086                 break;                
38087             }
38088         }
38089     },
38090     
38091     getBox : function(){
38092         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38093     },
38094     
38095     getMargins : function(){
38096         return this.margins;
38097     },
38098     
38099     updateBox : function(box){
38100         this.box = box;
38101         var el = this.activePanel.getEl();
38102         el.dom.style.left = box.x + "px";
38103         el.dom.style.top = box.y + "px";
38104         this.activePanel.setSize(box.width, box.height);
38105     },
38106     
38107     /**
38108      * Returns the container element for this region.
38109      * @return {Roo.Element}
38110      */
38111     getEl : function(){
38112         return this.activePanel;
38113     },
38114     
38115     /**
38116      * Returns true if this region is currently visible.
38117      * @return {Boolean}
38118      */
38119     isVisible : function(){
38120         return this.activePanel ? true : false;
38121     },
38122     
38123     setActivePanel : function(panel){
38124         panel = this.getPanel(panel);
38125         if(this.activePanel && this.activePanel != panel){
38126             this.activePanel.setActiveState(false);
38127             this.activePanel.getEl().setLeftTop(-10000,-10000);
38128         }
38129         this.activePanel = panel;
38130         panel.setActiveState(true);
38131         if(this.box){
38132             panel.setSize(this.box.width, this.box.height);
38133         }
38134         this.fireEvent("panelactivated", this, panel);
38135         this.fireEvent("invalidated");
38136     },
38137     
38138     /**
38139      * Show the specified panel.
38140      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38141      * @return {Roo.ContentPanel} The shown panel or null
38142      */
38143     showPanel : function(panel){
38144         panel = this.getPanel(panel);
38145         if(panel){
38146             this.setActivePanel(panel);
38147         }
38148         return panel;
38149     },
38150     
38151     /**
38152      * Get the active panel for this region.
38153      * @return {Roo.ContentPanel} The active panel or null
38154      */
38155     getActivePanel : function(){
38156         return this.activePanel;
38157     },
38158     
38159     /**
38160      * Add the passed ContentPanel(s)
38161      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38162      * @return {Roo.ContentPanel} The panel added (if only one was added)
38163      */
38164     add : function(panel){
38165         if(arguments.length > 1){
38166             for(var i = 0, len = arguments.length; i < len; i++) {
38167                 this.add(arguments[i]);
38168             }
38169             return null;
38170         }
38171         if(this.hasPanel(panel)){
38172             this.showPanel(panel);
38173             return panel;
38174         }
38175         var el = panel.getEl();
38176         if(el.dom.parentNode != this.mgr.el.dom){
38177             this.mgr.el.dom.appendChild(el.dom);
38178         }
38179         if(panel.setRegion){
38180             panel.setRegion(this);
38181         }
38182         this.panels.add(panel);
38183         el.setStyle("position", "absolute");
38184         if(!panel.background){
38185             this.setActivePanel(panel);
38186             if(this.config.initialSize && this.panels.getCount()==1){
38187                 this.resizeTo(this.config.initialSize);
38188             }
38189         }
38190         this.fireEvent("paneladded", this, panel);
38191         return panel;
38192     },
38193     
38194     /**
38195      * Returns true if the panel is in this region.
38196      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38197      * @return {Boolean}
38198      */
38199     hasPanel : function(panel){
38200         if(typeof panel == "object"){ // must be panel obj
38201             panel = panel.getId();
38202         }
38203         return this.getPanel(panel) ? true : false;
38204     },
38205     
38206     /**
38207      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38208      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38209      * @param {Boolean} preservePanel Overrides the config preservePanel option
38210      * @return {Roo.ContentPanel} The panel that was removed
38211      */
38212     remove : function(panel, preservePanel){
38213         panel = this.getPanel(panel);
38214         if(!panel){
38215             return null;
38216         }
38217         var e = {};
38218         this.fireEvent("beforeremove", this, panel, e);
38219         if(e.cancel === true){
38220             return null;
38221         }
38222         var panelId = panel.getId();
38223         this.panels.removeKey(panelId);
38224         return panel;
38225     },
38226     
38227     /**
38228      * Returns the panel specified or null if it's not in this region.
38229      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38230      * @return {Roo.ContentPanel}
38231      */
38232     getPanel : function(id){
38233         if(typeof id == "object"){ // must be panel obj
38234             return id;
38235         }
38236         return this.panels.get(id);
38237     },
38238     
38239     /**
38240      * Returns this regions position (north/south/east/west/center).
38241      * @return {String} 
38242      */
38243     getPosition: function(){
38244         return this.position;    
38245     }
38246 });/*
38247  * Based on:
38248  * Ext JS Library 1.1.1
38249  * Copyright(c) 2006-2007, Ext JS, LLC.
38250  *
38251  * Originally Released Under LGPL - original licence link has changed is not relivant.
38252  *
38253  * Fork - LGPL
38254  * <script type="text/javascript">
38255  */
38256  
38257 /**
38258  * @class Roo.bootstrap.layout.Region
38259  * @extends Roo.bootstrap.layout.Basic
38260  * This class represents a region in a layout manager.
38261  
38262  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38263  * @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})
38264  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38265  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38266  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38267  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38268  * @cfg {String}    title           The title for the region (overrides panel titles)
38269  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38270  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38271  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38272  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38273  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38274  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38275  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38276  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38277  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38278  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38279
38280  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38281  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38282  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38283  * @cfg {Number}    width           For East/West panels
38284  * @cfg {Number}    height          For North/South panels
38285  * @cfg {Boolean}   split           To show the splitter
38286  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38287  * 
38288  * @cfg {string}   cls             Extra CSS classes to add to region
38289  * 
38290  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38291  * @cfg {string}   region  the region that it inhabits..
38292  *
38293
38294  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38295  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38296
38297  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38298  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38299  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38300  */
38301 Roo.bootstrap.layout.Region = function(config)
38302 {
38303     this.applyConfig(config);
38304
38305     var mgr = config.mgr;
38306     var pos = config.region;
38307     config.skipConfig = true;
38308     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38309     
38310     if (mgr.el) {
38311         this.onRender(mgr.el);   
38312     }
38313      
38314     this.visible = true;
38315     this.collapsed = false;
38316     this.unrendered_panels = [];
38317 };
38318
38319 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38320
38321     position: '', // set by wrapper (eg. north/south etc..)
38322     unrendered_panels : null,  // unrendered panels.
38323     
38324     tabPosition : false,
38325     
38326     mgr: false, // points to 'Border'
38327     
38328     
38329     createBody : function(){
38330         /** This region's body element 
38331         * @type Roo.Element */
38332         this.bodyEl = this.el.createChild({
38333                 tag: "div",
38334                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38335         });
38336     },
38337
38338     onRender: function(ctr, pos)
38339     {
38340         var dh = Roo.DomHelper;
38341         /** This region's container element 
38342         * @type Roo.Element */
38343         this.el = dh.append(ctr.dom, {
38344                 tag: "div",
38345                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38346             }, true);
38347         /** This region's title element 
38348         * @type Roo.Element */
38349     
38350         this.titleEl = dh.append(this.el.dom,  {
38351                 tag: "div",
38352                 unselectable: "on",
38353                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38354                 children:[
38355                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38356                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38357                 ]
38358             }, true);
38359         
38360         this.titleEl.enableDisplayMode();
38361         /** This region's title text element 
38362         * @type HTMLElement */
38363         this.titleTextEl = this.titleEl.dom.firstChild;
38364         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38365         /*
38366         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38367         this.closeBtn.enableDisplayMode();
38368         this.closeBtn.on("click", this.closeClicked, this);
38369         this.closeBtn.hide();
38370     */
38371         this.createBody(this.config);
38372         if(this.config.hideWhenEmpty){
38373             this.hide();
38374             this.on("paneladded", this.validateVisibility, this);
38375             this.on("panelremoved", this.validateVisibility, this);
38376         }
38377         if(this.autoScroll){
38378             this.bodyEl.setStyle("overflow", "auto");
38379         }else{
38380             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38381         }
38382         //if(c.titlebar !== false){
38383             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38384                 this.titleEl.hide();
38385             }else{
38386                 this.titleEl.show();
38387                 if(this.config.title){
38388                     this.titleTextEl.innerHTML = this.config.title;
38389                 }
38390             }
38391         //}
38392         if(this.config.collapsed){
38393             this.collapse(true);
38394         }
38395         if(this.config.hidden){
38396             this.hide();
38397         }
38398         
38399         if (this.unrendered_panels && this.unrendered_panels.length) {
38400             for (var i =0;i< this.unrendered_panels.length; i++) {
38401                 this.add(this.unrendered_panels[i]);
38402             }
38403             this.unrendered_panels = null;
38404             
38405         }
38406         
38407     },
38408     
38409     applyConfig : function(c)
38410     {
38411         /*
38412          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38413             var dh = Roo.DomHelper;
38414             if(c.titlebar !== false){
38415                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38416                 this.collapseBtn.on("click", this.collapse, this);
38417                 this.collapseBtn.enableDisplayMode();
38418                 /*
38419                 if(c.showPin === true || this.showPin){
38420                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38421                     this.stickBtn.enableDisplayMode();
38422                     this.stickBtn.on("click", this.expand, this);
38423                     this.stickBtn.hide();
38424                 }
38425                 
38426             }
38427             */
38428             /** This region's collapsed element
38429             * @type Roo.Element */
38430             /*
38431              *
38432             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38433                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38434             ]}, true);
38435             
38436             if(c.floatable !== false){
38437                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38438                this.collapsedEl.on("click", this.collapseClick, this);
38439             }
38440
38441             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38442                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38443                    id: "message", unselectable: "on", style:{"float":"left"}});
38444                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38445              }
38446             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38447             this.expandBtn.on("click", this.expand, this);
38448             
38449         }
38450         
38451         if(this.collapseBtn){
38452             this.collapseBtn.setVisible(c.collapsible == true);
38453         }
38454         
38455         this.cmargins = c.cmargins || this.cmargins ||
38456                          (this.position == "west" || this.position == "east" ?
38457                              {top: 0, left: 2, right:2, bottom: 0} :
38458                              {top: 2, left: 0, right:0, bottom: 2});
38459         */
38460         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38461         
38462         
38463         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38464         
38465         this.autoScroll = c.autoScroll || false;
38466         
38467         
38468        
38469         
38470         this.duration = c.duration || .30;
38471         this.slideDuration = c.slideDuration || .45;
38472         this.config = c;
38473        
38474     },
38475     /**
38476      * Returns true if this region is currently visible.
38477      * @return {Boolean}
38478      */
38479     isVisible : function(){
38480         return this.visible;
38481     },
38482
38483     /**
38484      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38485      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38486      */
38487     //setCollapsedTitle : function(title){
38488     //    title = title || "&#160;";
38489      //   if(this.collapsedTitleTextEl){
38490       //      this.collapsedTitleTextEl.innerHTML = title;
38491        // }
38492     //},
38493
38494     getBox : function(){
38495         var b;
38496       //  if(!this.collapsed){
38497             b = this.el.getBox(false, true);
38498        // }else{
38499           //  b = this.collapsedEl.getBox(false, true);
38500         //}
38501         return b;
38502     },
38503
38504     getMargins : function(){
38505         return this.margins;
38506         //return this.collapsed ? this.cmargins : this.margins;
38507     },
38508 /*
38509     highlight : function(){
38510         this.el.addClass("x-layout-panel-dragover");
38511     },
38512
38513     unhighlight : function(){
38514         this.el.removeClass("x-layout-panel-dragover");
38515     },
38516 */
38517     updateBox : function(box)
38518     {
38519         if (!this.bodyEl) {
38520             return; // not rendered yet..
38521         }
38522         
38523         this.box = box;
38524         if(!this.collapsed){
38525             this.el.dom.style.left = box.x + "px";
38526             this.el.dom.style.top = box.y + "px";
38527             this.updateBody(box.width, box.height);
38528         }else{
38529             this.collapsedEl.dom.style.left = box.x + "px";
38530             this.collapsedEl.dom.style.top = box.y + "px";
38531             this.collapsedEl.setSize(box.width, box.height);
38532         }
38533         if(this.tabs){
38534             this.tabs.autoSizeTabs();
38535         }
38536     },
38537
38538     updateBody : function(w, h)
38539     {
38540         if(w !== null){
38541             this.el.setWidth(w);
38542             w -= this.el.getBorderWidth("rl");
38543             if(this.config.adjustments){
38544                 w += this.config.adjustments[0];
38545             }
38546         }
38547         if(h !== null && h > 0){
38548             this.el.setHeight(h);
38549             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38550             h -= this.el.getBorderWidth("tb");
38551             if(this.config.adjustments){
38552                 h += this.config.adjustments[1];
38553             }
38554             this.bodyEl.setHeight(h);
38555             if(this.tabs){
38556                 h = this.tabs.syncHeight(h);
38557             }
38558         }
38559         if(this.panelSize){
38560             w = w !== null ? w : this.panelSize.width;
38561             h = h !== null ? h : this.panelSize.height;
38562         }
38563         if(this.activePanel){
38564             var el = this.activePanel.getEl();
38565             w = w !== null ? w : el.getWidth();
38566             h = h !== null ? h : el.getHeight();
38567             this.panelSize = {width: w, height: h};
38568             this.activePanel.setSize(w, h);
38569         }
38570         if(Roo.isIE && this.tabs){
38571             this.tabs.el.repaint();
38572         }
38573     },
38574
38575     /**
38576      * Returns the container element for this region.
38577      * @return {Roo.Element}
38578      */
38579     getEl : function(){
38580         return this.el;
38581     },
38582
38583     /**
38584      * Hides this region.
38585      */
38586     hide : function(){
38587         //if(!this.collapsed){
38588             this.el.dom.style.left = "-2000px";
38589             this.el.hide();
38590         //}else{
38591          //   this.collapsedEl.dom.style.left = "-2000px";
38592          //   this.collapsedEl.hide();
38593        // }
38594         this.visible = false;
38595         this.fireEvent("visibilitychange", this, false);
38596     },
38597
38598     /**
38599      * Shows this region if it was previously hidden.
38600      */
38601     show : function(){
38602         //if(!this.collapsed){
38603             this.el.show();
38604         //}else{
38605         //    this.collapsedEl.show();
38606        // }
38607         this.visible = true;
38608         this.fireEvent("visibilitychange", this, true);
38609     },
38610 /*
38611     closeClicked : function(){
38612         if(this.activePanel){
38613             this.remove(this.activePanel);
38614         }
38615     },
38616
38617     collapseClick : function(e){
38618         if(this.isSlid){
38619            e.stopPropagation();
38620            this.slideIn();
38621         }else{
38622            e.stopPropagation();
38623            this.slideOut();
38624         }
38625     },
38626 */
38627     /**
38628      * Collapses this region.
38629      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38630      */
38631     /*
38632     collapse : function(skipAnim, skipCheck = false){
38633         if(this.collapsed) {
38634             return;
38635         }
38636         
38637         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38638             
38639             this.collapsed = true;
38640             if(this.split){
38641                 this.split.el.hide();
38642             }
38643             if(this.config.animate && skipAnim !== true){
38644                 this.fireEvent("invalidated", this);
38645                 this.animateCollapse();
38646             }else{
38647                 this.el.setLocation(-20000,-20000);
38648                 this.el.hide();
38649                 this.collapsedEl.show();
38650                 this.fireEvent("collapsed", this);
38651                 this.fireEvent("invalidated", this);
38652             }
38653         }
38654         
38655     },
38656 */
38657     animateCollapse : function(){
38658         // overridden
38659     },
38660
38661     /**
38662      * Expands this region if it was previously collapsed.
38663      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38664      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38665      */
38666     /*
38667     expand : function(e, skipAnim){
38668         if(e) {
38669             e.stopPropagation();
38670         }
38671         if(!this.collapsed || this.el.hasActiveFx()) {
38672             return;
38673         }
38674         if(this.isSlid){
38675             this.afterSlideIn();
38676             skipAnim = true;
38677         }
38678         this.collapsed = false;
38679         if(this.config.animate && skipAnim !== true){
38680             this.animateExpand();
38681         }else{
38682             this.el.show();
38683             if(this.split){
38684                 this.split.el.show();
38685             }
38686             this.collapsedEl.setLocation(-2000,-2000);
38687             this.collapsedEl.hide();
38688             this.fireEvent("invalidated", this);
38689             this.fireEvent("expanded", this);
38690         }
38691     },
38692 */
38693     animateExpand : function(){
38694         // overridden
38695     },
38696
38697     initTabs : function()
38698     {
38699         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38700         
38701         var ts = new Roo.bootstrap.panel.Tabs({
38702             el: this.bodyEl.dom,
38703             region : this,
38704             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38705             disableTooltips: this.config.disableTabTips,
38706             toolbar : this.config.toolbar
38707         });
38708         
38709         if(this.config.hideTabs){
38710             ts.stripWrap.setDisplayed(false);
38711         }
38712         this.tabs = ts;
38713         ts.resizeTabs = this.config.resizeTabs === true;
38714         ts.minTabWidth = this.config.minTabWidth || 40;
38715         ts.maxTabWidth = this.config.maxTabWidth || 250;
38716         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38717         ts.monitorResize = false;
38718         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38719         ts.bodyEl.addClass('roo-layout-tabs-body');
38720         this.panels.each(this.initPanelAsTab, this);
38721     },
38722
38723     initPanelAsTab : function(panel){
38724         var ti = this.tabs.addTab(
38725             panel.getEl().id,
38726             panel.getTitle(),
38727             null,
38728             this.config.closeOnTab && panel.isClosable(),
38729             panel.tpl
38730         );
38731         if(panel.tabTip !== undefined){
38732             ti.setTooltip(panel.tabTip);
38733         }
38734         ti.on("activate", function(){
38735               this.setActivePanel(panel);
38736         }, this);
38737         
38738         if(this.config.closeOnTab){
38739             ti.on("beforeclose", function(t, e){
38740                 e.cancel = true;
38741                 this.remove(panel);
38742             }, this);
38743         }
38744         
38745         panel.tabItem = ti;
38746         
38747         return ti;
38748     },
38749
38750     updatePanelTitle : function(panel, title)
38751     {
38752         if(this.activePanel == panel){
38753             this.updateTitle(title);
38754         }
38755         if(this.tabs){
38756             var ti = this.tabs.getTab(panel.getEl().id);
38757             ti.setText(title);
38758             if(panel.tabTip !== undefined){
38759                 ti.setTooltip(panel.tabTip);
38760             }
38761         }
38762     },
38763
38764     updateTitle : function(title){
38765         if(this.titleTextEl && !this.config.title){
38766             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38767         }
38768     },
38769
38770     setActivePanel : function(panel)
38771     {
38772         panel = this.getPanel(panel);
38773         if(this.activePanel && this.activePanel != panel){
38774             if(this.activePanel.setActiveState(false) === false){
38775                 return;
38776             }
38777         }
38778         this.activePanel = panel;
38779         panel.setActiveState(true);
38780         if(this.panelSize){
38781             panel.setSize(this.panelSize.width, this.panelSize.height);
38782         }
38783         if(this.closeBtn){
38784             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38785         }
38786         this.updateTitle(panel.getTitle());
38787         if(this.tabs){
38788             this.fireEvent("invalidated", this);
38789         }
38790         this.fireEvent("panelactivated", this, panel);
38791     },
38792
38793     /**
38794      * Shows the specified panel.
38795      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38796      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38797      */
38798     showPanel : function(panel)
38799     {
38800         panel = this.getPanel(panel);
38801         if(panel){
38802             if(this.tabs){
38803                 var tab = this.tabs.getTab(panel.getEl().id);
38804                 if(tab.isHidden()){
38805                     this.tabs.unhideTab(tab.id);
38806                 }
38807                 tab.activate();
38808             }else{
38809                 this.setActivePanel(panel);
38810             }
38811         }
38812         return panel;
38813     },
38814
38815     /**
38816      * Get the active panel for this region.
38817      * @return {Roo.ContentPanel} The active panel or null
38818      */
38819     getActivePanel : function(){
38820         return this.activePanel;
38821     },
38822
38823     validateVisibility : function(){
38824         if(this.panels.getCount() < 1){
38825             this.updateTitle("&#160;");
38826             this.closeBtn.hide();
38827             this.hide();
38828         }else{
38829             if(!this.isVisible()){
38830                 this.show();
38831             }
38832         }
38833     },
38834
38835     /**
38836      * Adds the passed ContentPanel(s) to this region.
38837      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38838      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38839      */
38840     add : function(panel)
38841     {
38842         if(arguments.length > 1){
38843             for(var i = 0, len = arguments.length; i < len; i++) {
38844                 this.add(arguments[i]);
38845             }
38846             return null;
38847         }
38848         
38849         // if we have not been rendered yet, then we can not really do much of this..
38850         if (!this.bodyEl) {
38851             this.unrendered_panels.push(panel);
38852             return panel;
38853         }
38854         
38855         
38856         
38857         
38858         if(this.hasPanel(panel)){
38859             this.showPanel(panel);
38860             return panel;
38861         }
38862         panel.setRegion(this);
38863         this.panels.add(panel);
38864        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38865             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38866             // and hide them... ???
38867             this.bodyEl.dom.appendChild(panel.getEl().dom);
38868             if(panel.background !== true){
38869                 this.setActivePanel(panel);
38870             }
38871             this.fireEvent("paneladded", this, panel);
38872             return panel;
38873         }
38874         */
38875         if(!this.tabs){
38876             this.initTabs();
38877         }else{
38878             this.initPanelAsTab(panel);
38879         }
38880         
38881         
38882         if(panel.background !== true){
38883             this.tabs.activate(panel.getEl().id);
38884         }
38885         this.fireEvent("paneladded", this, panel);
38886         return panel;
38887     },
38888
38889     /**
38890      * Hides the tab for the specified panel.
38891      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38892      */
38893     hidePanel : function(panel){
38894         if(this.tabs && (panel = this.getPanel(panel))){
38895             this.tabs.hideTab(panel.getEl().id);
38896         }
38897     },
38898
38899     /**
38900      * Unhides the tab for a previously hidden panel.
38901      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38902      */
38903     unhidePanel : function(panel){
38904         if(this.tabs && (panel = this.getPanel(panel))){
38905             this.tabs.unhideTab(panel.getEl().id);
38906         }
38907     },
38908
38909     clearPanels : function(){
38910         while(this.panels.getCount() > 0){
38911              this.remove(this.panels.first());
38912         }
38913     },
38914
38915     /**
38916      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38917      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38918      * @param {Boolean} preservePanel Overrides the config preservePanel option
38919      * @return {Roo.ContentPanel} The panel that was removed
38920      */
38921     remove : function(panel, preservePanel)
38922     {
38923         panel = this.getPanel(panel);
38924         if(!panel){
38925             return null;
38926         }
38927         var e = {};
38928         this.fireEvent("beforeremove", this, panel, e);
38929         if(e.cancel === true){
38930             return null;
38931         }
38932         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38933         var panelId = panel.getId();
38934         this.panels.removeKey(panelId);
38935         if(preservePanel){
38936             document.body.appendChild(panel.getEl().dom);
38937         }
38938         if(this.tabs){
38939             this.tabs.removeTab(panel.getEl().id);
38940         }else if (!preservePanel){
38941             this.bodyEl.dom.removeChild(panel.getEl().dom);
38942         }
38943         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38944             var p = this.panels.first();
38945             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38946             tempEl.appendChild(p.getEl().dom);
38947             this.bodyEl.update("");
38948             this.bodyEl.dom.appendChild(p.getEl().dom);
38949             tempEl = null;
38950             this.updateTitle(p.getTitle());
38951             this.tabs = null;
38952             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38953             this.setActivePanel(p);
38954         }
38955         panel.setRegion(null);
38956         if(this.activePanel == panel){
38957             this.activePanel = null;
38958         }
38959         if(this.config.autoDestroy !== false && preservePanel !== true){
38960             try{panel.destroy();}catch(e){}
38961         }
38962         this.fireEvent("panelremoved", this, panel);
38963         return panel;
38964     },
38965
38966     /**
38967      * Returns the TabPanel component used by this region
38968      * @return {Roo.TabPanel}
38969      */
38970     getTabs : function(){
38971         return this.tabs;
38972     },
38973
38974     createTool : function(parentEl, className){
38975         var btn = Roo.DomHelper.append(parentEl, {
38976             tag: "div",
38977             cls: "x-layout-tools-button",
38978             children: [ {
38979                 tag: "div",
38980                 cls: "roo-layout-tools-button-inner " + className,
38981                 html: "&#160;"
38982             }]
38983         }, true);
38984         btn.addClassOnOver("roo-layout-tools-button-over");
38985         return btn;
38986     }
38987 });/*
38988  * Based on:
38989  * Ext JS Library 1.1.1
38990  * Copyright(c) 2006-2007, Ext JS, LLC.
38991  *
38992  * Originally Released Under LGPL - original licence link has changed is not relivant.
38993  *
38994  * Fork - LGPL
38995  * <script type="text/javascript">
38996  */
38997  
38998
38999
39000 /**
39001  * @class Roo.SplitLayoutRegion
39002  * @extends Roo.LayoutRegion
39003  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39004  */
39005 Roo.bootstrap.layout.Split = function(config){
39006     this.cursor = config.cursor;
39007     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39008 };
39009
39010 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39011 {
39012     splitTip : "Drag to resize.",
39013     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39014     useSplitTips : false,
39015
39016     applyConfig : function(config){
39017         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39018     },
39019     
39020     onRender : function(ctr,pos) {
39021         
39022         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39023         if(!this.config.split){
39024             return;
39025         }
39026         if(!this.split){
39027             
39028             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39029                             tag: "div",
39030                             id: this.el.id + "-split",
39031                             cls: "roo-layout-split roo-layout-split-"+this.position,
39032                             html: "&#160;"
39033             });
39034             /** The SplitBar for this region 
39035             * @type Roo.SplitBar */
39036             // does not exist yet...
39037             Roo.log([this.position, this.orientation]);
39038             
39039             this.split = new Roo.bootstrap.SplitBar({
39040                 dragElement : splitEl,
39041                 resizingElement: this.el,
39042                 orientation : this.orientation
39043             });
39044             
39045             this.split.on("moved", this.onSplitMove, this);
39046             this.split.useShim = this.config.useShim === true;
39047             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39048             if(this.useSplitTips){
39049                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39050             }
39051             //if(config.collapsible){
39052             //    this.split.el.on("dblclick", this.collapse,  this);
39053             //}
39054         }
39055         if(typeof this.config.minSize != "undefined"){
39056             this.split.minSize = this.config.minSize;
39057         }
39058         if(typeof this.config.maxSize != "undefined"){
39059             this.split.maxSize = this.config.maxSize;
39060         }
39061         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39062             this.hideSplitter();
39063         }
39064         
39065     },
39066
39067     getHMaxSize : function(){
39068          var cmax = this.config.maxSize || 10000;
39069          var center = this.mgr.getRegion("center");
39070          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39071     },
39072
39073     getVMaxSize : function(){
39074          var cmax = this.config.maxSize || 10000;
39075          var center = this.mgr.getRegion("center");
39076          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39077     },
39078
39079     onSplitMove : function(split, newSize){
39080         this.fireEvent("resized", this, newSize);
39081     },
39082     
39083     /** 
39084      * Returns the {@link Roo.SplitBar} for this region.
39085      * @return {Roo.SplitBar}
39086      */
39087     getSplitBar : function(){
39088         return this.split;
39089     },
39090     
39091     hide : function(){
39092         this.hideSplitter();
39093         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39094     },
39095
39096     hideSplitter : function(){
39097         if(this.split){
39098             this.split.el.setLocation(-2000,-2000);
39099             this.split.el.hide();
39100         }
39101     },
39102
39103     show : function(){
39104         if(this.split){
39105             this.split.el.show();
39106         }
39107         Roo.bootstrap.layout.Split.superclass.show.call(this);
39108     },
39109     
39110     beforeSlide: function(){
39111         if(Roo.isGecko){// firefox overflow auto bug workaround
39112             this.bodyEl.clip();
39113             if(this.tabs) {
39114                 this.tabs.bodyEl.clip();
39115             }
39116             if(this.activePanel){
39117                 this.activePanel.getEl().clip();
39118                 
39119                 if(this.activePanel.beforeSlide){
39120                     this.activePanel.beforeSlide();
39121                 }
39122             }
39123         }
39124     },
39125     
39126     afterSlide : function(){
39127         if(Roo.isGecko){// firefox overflow auto bug workaround
39128             this.bodyEl.unclip();
39129             if(this.tabs) {
39130                 this.tabs.bodyEl.unclip();
39131             }
39132             if(this.activePanel){
39133                 this.activePanel.getEl().unclip();
39134                 if(this.activePanel.afterSlide){
39135                     this.activePanel.afterSlide();
39136                 }
39137             }
39138         }
39139     },
39140
39141     initAutoHide : function(){
39142         if(this.autoHide !== false){
39143             if(!this.autoHideHd){
39144                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39145                 this.autoHideHd = {
39146                     "mouseout": function(e){
39147                         if(!e.within(this.el, true)){
39148                             st.delay(500);
39149                         }
39150                     },
39151                     "mouseover" : function(e){
39152                         st.cancel();
39153                     },
39154                     scope : this
39155                 };
39156             }
39157             this.el.on(this.autoHideHd);
39158         }
39159     },
39160
39161     clearAutoHide : function(){
39162         if(this.autoHide !== false){
39163             this.el.un("mouseout", this.autoHideHd.mouseout);
39164             this.el.un("mouseover", this.autoHideHd.mouseover);
39165         }
39166     },
39167
39168     clearMonitor : function(){
39169         Roo.get(document).un("click", this.slideInIf, this);
39170     },
39171
39172     // these names are backwards but not changed for compat
39173     slideOut : function(){
39174         if(this.isSlid || this.el.hasActiveFx()){
39175             return;
39176         }
39177         this.isSlid = true;
39178         if(this.collapseBtn){
39179             this.collapseBtn.hide();
39180         }
39181         this.closeBtnState = this.closeBtn.getStyle('display');
39182         this.closeBtn.hide();
39183         if(this.stickBtn){
39184             this.stickBtn.show();
39185         }
39186         this.el.show();
39187         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39188         this.beforeSlide();
39189         this.el.setStyle("z-index", 10001);
39190         this.el.slideIn(this.getSlideAnchor(), {
39191             callback: function(){
39192                 this.afterSlide();
39193                 this.initAutoHide();
39194                 Roo.get(document).on("click", this.slideInIf, this);
39195                 this.fireEvent("slideshow", this);
39196             },
39197             scope: this,
39198             block: true
39199         });
39200     },
39201
39202     afterSlideIn : function(){
39203         this.clearAutoHide();
39204         this.isSlid = false;
39205         this.clearMonitor();
39206         this.el.setStyle("z-index", "");
39207         if(this.collapseBtn){
39208             this.collapseBtn.show();
39209         }
39210         this.closeBtn.setStyle('display', this.closeBtnState);
39211         if(this.stickBtn){
39212             this.stickBtn.hide();
39213         }
39214         this.fireEvent("slidehide", this);
39215     },
39216
39217     slideIn : function(cb){
39218         if(!this.isSlid || this.el.hasActiveFx()){
39219             Roo.callback(cb);
39220             return;
39221         }
39222         this.isSlid = false;
39223         this.beforeSlide();
39224         this.el.slideOut(this.getSlideAnchor(), {
39225             callback: function(){
39226                 this.el.setLeftTop(-10000, -10000);
39227                 this.afterSlide();
39228                 this.afterSlideIn();
39229                 Roo.callback(cb);
39230             },
39231             scope: this,
39232             block: true
39233         });
39234     },
39235     
39236     slideInIf : function(e){
39237         if(!e.within(this.el)){
39238             this.slideIn();
39239         }
39240     },
39241
39242     animateCollapse : function(){
39243         this.beforeSlide();
39244         this.el.setStyle("z-index", 20000);
39245         var anchor = this.getSlideAnchor();
39246         this.el.slideOut(anchor, {
39247             callback : function(){
39248                 this.el.setStyle("z-index", "");
39249                 this.collapsedEl.slideIn(anchor, {duration:.3});
39250                 this.afterSlide();
39251                 this.el.setLocation(-10000,-10000);
39252                 this.el.hide();
39253                 this.fireEvent("collapsed", this);
39254             },
39255             scope: this,
39256             block: true
39257         });
39258     },
39259
39260     animateExpand : function(){
39261         this.beforeSlide();
39262         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39263         this.el.setStyle("z-index", 20000);
39264         this.collapsedEl.hide({
39265             duration:.1
39266         });
39267         this.el.slideIn(this.getSlideAnchor(), {
39268             callback : function(){
39269                 this.el.setStyle("z-index", "");
39270                 this.afterSlide();
39271                 if(this.split){
39272                     this.split.el.show();
39273                 }
39274                 this.fireEvent("invalidated", this);
39275                 this.fireEvent("expanded", this);
39276             },
39277             scope: this,
39278             block: true
39279         });
39280     },
39281
39282     anchors : {
39283         "west" : "left",
39284         "east" : "right",
39285         "north" : "top",
39286         "south" : "bottom"
39287     },
39288
39289     sanchors : {
39290         "west" : "l",
39291         "east" : "r",
39292         "north" : "t",
39293         "south" : "b"
39294     },
39295
39296     canchors : {
39297         "west" : "tl-tr",
39298         "east" : "tr-tl",
39299         "north" : "tl-bl",
39300         "south" : "bl-tl"
39301     },
39302
39303     getAnchor : function(){
39304         return this.anchors[this.position];
39305     },
39306
39307     getCollapseAnchor : function(){
39308         return this.canchors[this.position];
39309     },
39310
39311     getSlideAnchor : function(){
39312         return this.sanchors[this.position];
39313     },
39314
39315     getAlignAdj : function(){
39316         var cm = this.cmargins;
39317         switch(this.position){
39318             case "west":
39319                 return [0, 0];
39320             break;
39321             case "east":
39322                 return [0, 0];
39323             break;
39324             case "north":
39325                 return [0, 0];
39326             break;
39327             case "south":
39328                 return [0, 0];
39329             break;
39330         }
39331     },
39332
39333     getExpandAdj : function(){
39334         var c = this.collapsedEl, cm = this.cmargins;
39335         switch(this.position){
39336             case "west":
39337                 return [-(cm.right+c.getWidth()+cm.left), 0];
39338             break;
39339             case "east":
39340                 return [cm.right+c.getWidth()+cm.left, 0];
39341             break;
39342             case "north":
39343                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39344             break;
39345             case "south":
39346                 return [0, cm.top+cm.bottom+c.getHeight()];
39347             break;
39348         }
39349     }
39350 });/*
39351  * Based on:
39352  * Ext JS Library 1.1.1
39353  * Copyright(c) 2006-2007, Ext JS, LLC.
39354  *
39355  * Originally Released Under LGPL - original licence link has changed is not relivant.
39356  *
39357  * Fork - LGPL
39358  * <script type="text/javascript">
39359  */
39360 /*
39361  * These classes are private internal classes
39362  */
39363 Roo.bootstrap.layout.Center = function(config){
39364     config.region = "center";
39365     Roo.bootstrap.layout.Region.call(this, config);
39366     this.visible = true;
39367     this.minWidth = config.minWidth || 20;
39368     this.minHeight = config.minHeight || 20;
39369 };
39370
39371 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39372     hide : function(){
39373         // center panel can't be hidden
39374     },
39375     
39376     show : function(){
39377         // center panel can't be hidden
39378     },
39379     
39380     getMinWidth: function(){
39381         return this.minWidth;
39382     },
39383     
39384     getMinHeight: function(){
39385         return this.minHeight;
39386     }
39387 });
39388
39389
39390
39391
39392  
39393
39394
39395
39396
39397
39398
39399 Roo.bootstrap.layout.North = function(config)
39400 {
39401     config.region = 'north';
39402     config.cursor = 'n-resize';
39403     
39404     Roo.bootstrap.layout.Split.call(this, config);
39405     
39406     
39407     if(this.split){
39408         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39409         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39410         this.split.el.addClass("roo-layout-split-v");
39411     }
39412     //var size = config.initialSize || config.height;
39413     //if(this.el && typeof size != "undefined"){
39414     //    this.el.setHeight(size);
39415     //}
39416 };
39417 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39418 {
39419     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39420      
39421      
39422     onRender : function(ctr, pos)
39423     {
39424         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39425         var size = this.config.initialSize || this.config.height;
39426         if(this.el && typeof size != "undefined"){
39427             this.el.setHeight(size);
39428         }
39429     
39430     },
39431     
39432     getBox : function(){
39433         if(this.collapsed){
39434             return this.collapsedEl.getBox();
39435         }
39436         var box = this.el.getBox();
39437         if(this.split){
39438             box.height += this.split.el.getHeight();
39439         }
39440         return box;
39441     },
39442     
39443     updateBox : function(box){
39444         if(this.split && !this.collapsed){
39445             box.height -= this.split.el.getHeight();
39446             this.split.el.setLeft(box.x);
39447             this.split.el.setTop(box.y+box.height);
39448             this.split.el.setWidth(box.width);
39449         }
39450         if(this.collapsed){
39451             this.updateBody(box.width, null);
39452         }
39453         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39454     }
39455 });
39456
39457
39458
39459
39460
39461 Roo.bootstrap.layout.South = function(config){
39462     config.region = 'south';
39463     config.cursor = 's-resize';
39464     Roo.bootstrap.layout.Split.call(this, config);
39465     if(this.split){
39466         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39467         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39468         this.split.el.addClass("roo-layout-split-v");
39469     }
39470     
39471 };
39472
39473 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39474     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39475     
39476     onRender : function(ctr, pos)
39477     {
39478         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39479         var size = this.config.initialSize || this.config.height;
39480         if(this.el && typeof size != "undefined"){
39481             this.el.setHeight(size);
39482         }
39483     
39484     },
39485     
39486     getBox : function(){
39487         if(this.collapsed){
39488             return this.collapsedEl.getBox();
39489         }
39490         var box = this.el.getBox();
39491         if(this.split){
39492             var sh = this.split.el.getHeight();
39493             box.height += sh;
39494             box.y -= sh;
39495         }
39496         return box;
39497     },
39498     
39499     updateBox : function(box){
39500         if(this.split && !this.collapsed){
39501             var sh = this.split.el.getHeight();
39502             box.height -= sh;
39503             box.y += sh;
39504             this.split.el.setLeft(box.x);
39505             this.split.el.setTop(box.y-sh);
39506             this.split.el.setWidth(box.width);
39507         }
39508         if(this.collapsed){
39509             this.updateBody(box.width, null);
39510         }
39511         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39512     }
39513 });
39514
39515 Roo.bootstrap.layout.East = function(config){
39516     config.region = "east";
39517     config.cursor = "e-resize";
39518     Roo.bootstrap.layout.Split.call(this, config);
39519     if(this.split){
39520         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39521         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39522         this.split.el.addClass("roo-layout-split-h");
39523     }
39524     
39525 };
39526 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39527     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39528     
39529     onRender : function(ctr, pos)
39530     {
39531         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39532         var size = this.config.initialSize || this.config.width;
39533         if(this.el && typeof size != "undefined"){
39534             this.el.setWidth(size);
39535         }
39536     
39537     },
39538     
39539     getBox : function(){
39540         if(this.collapsed){
39541             return this.collapsedEl.getBox();
39542         }
39543         var box = this.el.getBox();
39544         if(this.split){
39545             var sw = this.split.el.getWidth();
39546             box.width += sw;
39547             box.x -= sw;
39548         }
39549         return box;
39550     },
39551
39552     updateBox : function(box){
39553         if(this.split && !this.collapsed){
39554             var sw = this.split.el.getWidth();
39555             box.width -= sw;
39556             this.split.el.setLeft(box.x);
39557             this.split.el.setTop(box.y);
39558             this.split.el.setHeight(box.height);
39559             box.x += sw;
39560         }
39561         if(this.collapsed){
39562             this.updateBody(null, box.height);
39563         }
39564         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39565     }
39566 });
39567
39568 Roo.bootstrap.layout.West = function(config){
39569     config.region = "west";
39570     config.cursor = "w-resize";
39571     
39572     Roo.bootstrap.layout.Split.call(this, config);
39573     if(this.split){
39574         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39575         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39576         this.split.el.addClass("roo-layout-split-h");
39577     }
39578     
39579 };
39580 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39581     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39582     
39583     onRender: function(ctr, pos)
39584     {
39585         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39586         var size = this.config.initialSize || this.config.width;
39587         if(typeof size != "undefined"){
39588             this.el.setWidth(size);
39589         }
39590     },
39591     
39592     getBox : function(){
39593         if(this.collapsed){
39594             return this.collapsedEl.getBox();
39595         }
39596         var box = this.el.getBox();
39597         if (box.width == 0) {
39598             box.width = this.config.width; // kludge?
39599         }
39600         if(this.split){
39601             box.width += this.split.el.getWidth();
39602         }
39603         return box;
39604     },
39605     
39606     updateBox : function(box){
39607         if(this.split && !this.collapsed){
39608             var sw = this.split.el.getWidth();
39609             box.width -= sw;
39610             this.split.el.setLeft(box.x+box.width);
39611             this.split.el.setTop(box.y);
39612             this.split.el.setHeight(box.height);
39613         }
39614         if(this.collapsed){
39615             this.updateBody(null, box.height);
39616         }
39617         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39618     }
39619 });Roo.namespace("Roo.bootstrap.panel");/*
39620  * Based on:
39621  * Ext JS Library 1.1.1
39622  * Copyright(c) 2006-2007, Ext JS, LLC.
39623  *
39624  * Originally Released Under LGPL - original licence link has changed is not relivant.
39625  *
39626  * Fork - LGPL
39627  * <script type="text/javascript">
39628  */
39629 /**
39630  * @class Roo.ContentPanel
39631  * @extends Roo.util.Observable
39632  * A basic ContentPanel element.
39633  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39634  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39635  * @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
39636  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39637  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39638  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39639  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39640  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39641  * @cfg {String} title          The title for this panel
39642  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39643  * @cfg {String} url            Calls {@link #setUrl} with this value
39644  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39645  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39646  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39647  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39648  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39649  * @cfg {Boolean} badges render the badges
39650  * @cfg {String} cls  extra classes to use  
39651  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39652
39653  * @constructor
39654  * Create a new ContentPanel.
39655  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39656  * @param {String/Object} config A string to set only the title or a config object
39657  * @param {String} content (optional) Set the HTML content for this panel
39658  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39659  */
39660 Roo.bootstrap.panel.Content = function( config){
39661     
39662     this.tpl = config.tpl || false;
39663     
39664     var el = config.el;
39665     var content = config.content;
39666
39667     if(config.autoCreate){ // xtype is available if this is called from factory
39668         el = Roo.id();
39669     }
39670     this.el = Roo.get(el);
39671     if(!this.el && config && config.autoCreate){
39672         if(typeof config.autoCreate == "object"){
39673             if(!config.autoCreate.id){
39674                 config.autoCreate.id = config.id||el;
39675             }
39676             this.el = Roo.DomHelper.append(document.body,
39677                         config.autoCreate, true);
39678         }else{
39679             var elcfg =  {
39680                 tag: "div",
39681                 cls: (config.cls || '') +
39682                     (config.background ? ' bg-' + config.background : '') +
39683                     " roo-layout-inactive-content",
39684                 id: config.id||el
39685             };
39686             if (config.iframe) {
39687                 elcfg.cn = [
39688                     {
39689                         tag : 'iframe',
39690                         style : 'border: 0px',
39691                         src : 'about:blank'
39692                     }
39693                 ];
39694             }
39695               
39696             if (config.html) {
39697                 elcfg.html = config.html;
39698                 
39699             }
39700                         
39701             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39702             if (config.iframe) {
39703                 this.iframeEl = this.el.select('iframe',true).first();
39704             }
39705             
39706         }
39707     } 
39708     this.closable = false;
39709     this.loaded = false;
39710     this.active = false;
39711    
39712       
39713     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39714         
39715         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39716         
39717         this.wrapEl = this.el; //this.el.wrap();
39718         var ti = [];
39719         if (config.toolbar.items) {
39720             ti = config.toolbar.items ;
39721             delete config.toolbar.items ;
39722         }
39723         
39724         var nitems = [];
39725         this.toolbar.render(this.wrapEl, 'before');
39726         for(var i =0;i < ti.length;i++) {
39727           //  Roo.log(['add child', items[i]]);
39728             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39729         }
39730         this.toolbar.items = nitems;
39731         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39732         delete config.toolbar;
39733         
39734     }
39735     /*
39736     // xtype created footer. - not sure if will work as we normally have to render first..
39737     if (this.footer && !this.footer.el && this.footer.xtype) {
39738         if (!this.wrapEl) {
39739             this.wrapEl = this.el.wrap();
39740         }
39741     
39742         this.footer.container = this.wrapEl.createChild();
39743          
39744         this.footer = Roo.factory(this.footer, Roo);
39745         
39746     }
39747     */
39748     
39749      if(typeof config == "string"){
39750         this.title = config;
39751     }else{
39752         Roo.apply(this, config);
39753     }
39754     
39755     if(this.resizeEl){
39756         this.resizeEl = Roo.get(this.resizeEl, true);
39757     }else{
39758         this.resizeEl = this.el;
39759     }
39760     // handle view.xtype
39761     
39762  
39763     
39764     
39765     this.addEvents({
39766         /**
39767          * @event activate
39768          * Fires when this panel is activated. 
39769          * @param {Roo.ContentPanel} this
39770          */
39771         "activate" : true,
39772         /**
39773          * @event deactivate
39774          * Fires when this panel is activated. 
39775          * @param {Roo.ContentPanel} this
39776          */
39777         "deactivate" : true,
39778
39779         /**
39780          * @event resize
39781          * Fires when this panel is resized if fitToFrame is true.
39782          * @param {Roo.ContentPanel} this
39783          * @param {Number} width The width after any component adjustments
39784          * @param {Number} height The height after any component adjustments
39785          */
39786         "resize" : true,
39787         
39788          /**
39789          * @event render
39790          * Fires when this tab is created
39791          * @param {Roo.ContentPanel} this
39792          */
39793         "render" : true
39794         
39795         
39796         
39797     });
39798     
39799
39800     
39801     
39802     if(this.autoScroll && !this.iframe){
39803         this.resizeEl.setStyle("overflow", "auto");
39804     } else {
39805         // fix randome scrolling
39806         //this.el.on('scroll', function() {
39807         //    Roo.log('fix random scolling');
39808         //    this.scrollTo('top',0); 
39809         //});
39810     }
39811     content = content || this.content;
39812     if(content){
39813         this.setContent(content);
39814     }
39815     if(config && config.url){
39816         this.setUrl(this.url, this.params, this.loadOnce);
39817     }
39818     
39819     
39820     
39821     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39822     
39823     if (this.view && typeof(this.view.xtype) != 'undefined') {
39824         this.view.el = this.el.appendChild(document.createElement("div"));
39825         this.view = Roo.factory(this.view); 
39826         this.view.render  &&  this.view.render(false, '');  
39827     }
39828     
39829     
39830     this.fireEvent('render', this);
39831 };
39832
39833 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39834     
39835     cls : '',
39836     background : '',
39837     
39838     tabTip : '',
39839     
39840     iframe : false,
39841     iframeEl : false,
39842     
39843     setRegion : function(region){
39844         this.region = region;
39845         this.setActiveClass(region && !this.background);
39846     },
39847     
39848     
39849     setActiveClass: function(state)
39850     {
39851         if(state){
39852            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39853            this.el.setStyle('position','relative');
39854         }else{
39855            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39856            this.el.setStyle('position', 'absolute');
39857         } 
39858     },
39859     
39860     /**
39861      * Returns the toolbar for this Panel if one was configured. 
39862      * @return {Roo.Toolbar} 
39863      */
39864     getToolbar : function(){
39865         return this.toolbar;
39866     },
39867     
39868     setActiveState : function(active)
39869     {
39870         this.active = active;
39871         this.setActiveClass(active);
39872         if(!active){
39873             if(this.fireEvent("deactivate", this) === false){
39874                 return false;
39875             }
39876             return true;
39877         }
39878         this.fireEvent("activate", this);
39879         return true;
39880     },
39881     /**
39882      * Updates this panel's element (not for iframe)
39883      * @param {String} content The new content
39884      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39885     */
39886     setContent : function(content, loadScripts){
39887         if (this.iframe) {
39888             return;
39889         }
39890         
39891         this.el.update(content, loadScripts);
39892     },
39893
39894     ignoreResize : function(w, h){
39895         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39896             return true;
39897         }else{
39898             this.lastSize = {width: w, height: h};
39899             return false;
39900         }
39901     },
39902     /**
39903      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39904      * @return {Roo.UpdateManager} The UpdateManager
39905      */
39906     getUpdateManager : function(){
39907         if (this.iframe) {
39908             return false;
39909         }
39910         return this.el.getUpdateManager();
39911     },
39912      /**
39913      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39914      * Does not work with IFRAME contents
39915      * @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:
39916 <pre><code>
39917 panel.load({
39918     url: "your-url.php",
39919     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39920     callback: yourFunction,
39921     scope: yourObject, //(optional scope)
39922     discardUrl: false,
39923     nocache: false,
39924     text: "Loading...",
39925     timeout: 30,
39926     scripts: false
39927 });
39928 </code></pre>
39929      
39930      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39931      * 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.
39932      * @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}
39933      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39934      * @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.
39935      * @return {Roo.ContentPanel} this
39936      */
39937     load : function(){
39938         
39939         if (this.iframe) {
39940             return this;
39941         }
39942         
39943         var um = this.el.getUpdateManager();
39944         um.update.apply(um, arguments);
39945         return this;
39946     },
39947
39948
39949     /**
39950      * 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.
39951      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39952      * @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)
39953      * @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)
39954      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39955      */
39956     setUrl : function(url, params, loadOnce){
39957         if (this.iframe) {
39958             this.iframeEl.dom.src = url;
39959             return false;
39960         }
39961         
39962         if(this.refreshDelegate){
39963             this.removeListener("activate", this.refreshDelegate);
39964         }
39965         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39966         this.on("activate", this.refreshDelegate);
39967         return this.el.getUpdateManager();
39968     },
39969     
39970     _handleRefresh : function(url, params, loadOnce){
39971         if(!loadOnce || !this.loaded){
39972             var updater = this.el.getUpdateManager();
39973             updater.update(url, params, this._setLoaded.createDelegate(this));
39974         }
39975     },
39976     
39977     _setLoaded : function(){
39978         this.loaded = true;
39979     }, 
39980     
39981     /**
39982      * Returns this panel's id
39983      * @return {String} 
39984      */
39985     getId : function(){
39986         return this.el.id;
39987     },
39988     
39989     /** 
39990      * Returns this panel's element - used by regiosn to add.
39991      * @return {Roo.Element} 
39992      */
39993     getEl : function(){
39994         return this.wrapEl || this.el;
39995     },
39996     
39997    
39998     
39999     adjustForComponents : function(width, height)
40000     {
40001         //Roo.log('adjustForComponents ');
40002         if(this.resizeEl != this.el){
40003             width -= this.el.getFrameWidth('lr');
40004             height -= this.el.getFrameWidth('tb');
40005         }
40006         if(this.toolbar){
40007             var te = this.toolbar.getEl();
40008             te.setWidth(width);
40009             height -= te.getHeight();
40010         }
40011         if(this.footer){
40012             var te = this.footer.getEl();
40013             te.setWidth(width);
40014             height -= te.getHeight();
40015         }
40016         
40017         
40018         if(this.adjustments){
40019             width += this.adjustments[0];
40020             height += this.adjustments[1];
40021         }
40022         return {"width": width, "height": height};
40023     },
40024     
40025     setSize : function(width, height){
40026         if(this.fitToFrame && !this.ignoreResize(width, height)){
40027             if(this.fitContainer && this.resizeEl != this.el){
40028                 this.el.setSize(width, height);
40029             }
40030             var size = this.adjustForComponents(width, height);
40031             if (this.iframe) {
40032                 this.iframeEl.setSize(width,height);
40033             }
40034             
40035             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40036             this.fireEvent('resize', this, size.width, size.height);
40037             
40038             
40039         }
40040     },
40041     
40042     /**
40043      * Returns this panel's title
40044      * @return {String} 
40045      */
40046     getTitle : function(){
40047         
40048         if (typeof(this.title) != 'object') {
40049             return this.title;
40050         }
40051         
40052         var t = '';
40053         for (var k in this.title) {
40054             if (!this.title.hasOwnProperty(k)) {
40055                 continue;
40056             }
40057             
40058             if (k.indexOf('-') >= 0) {
40059                 var s = k.split('-');
40060                 for (var i = 0; i<s.length; i++) {
40061                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40062                 }
40063             } else {
40064                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40065             }
40066         }
40067         return t;
40068     },
40069     
40070     /**
40071      * Set this panel's title
40072      * @param {String} title
40073      */
40074     setTitle : function(title){
40075         this.title = title;
40076         if(this.region){
40077             this.region.updatePanelTitle(this, title);
40078         }
40079     },
40080     
40081     /**
40082      * Returns true is this panel was configured to be closable
40083      * @return {Boolean} 
40084      */
40085     isClosable : function(){
40086         return this.closable;
40087     },
40088     
40089     beforeSlide : function(){
40090         this.el.clip();
40091         this.resizeEl.clip();
40092     },
40093     
40094     afterSlide : function(){
40095         this.el.unclip();
40096         this.resizeEl.unclip();
40097     },
40098     
40099     /**
40100      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40101      *   Will fail silently if the {@link #setUrl} method has not been called.
40102      *   This does not activate the panel, just updates its content.
40103      */
40104     refresh : function(){
40105         if(this.refreshDelegate){
40106            this.loaded = false;
40107            this.refreshDelegate();
40108         }
40109     },
40110     
40111     /**
40112      * Destroys this panel
40113      */
40114     destroy : function(){
40115         this.el.removeAllListeners();
40116         var tempEl = document.createElement("span");
40117         tempEl.appendChild(this.el.dom);
40118         tempEl.innerHTML = "";
40119         this.el.remove();
40120         this.el = null;
40121     },
40122     
40123     /**
40124      * form - if the content panel contains a form - this is a reference to it.
40125      * @type {Roo.form.Form}
40126      */
40127     form : false,
40128     /**
40129      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40130      *    This contains a reference to it.
40131      * @type {Roo.View}
40132      */
40133     view : false,
40134     
40135       /**
40136      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40137      * <pre><code>
40138
40139 layout.addxtype({
40140        xtype : 'Form',
40141        items: [ .... ]
40142    }
40143 );
40144
40145 </code></pre>
40146      * @param {Object} cfg Xtype definition of item to add.
40147      */
40148     
40149     
40150     getChildContainer: function () {
40151         return this.getEl();
40152     }
40153     
40154     
40155     /*
40156         var  ret = new Roo.factory(cfg);
40157         return ret;
40158         
40159         
40160         // add form..
40161         if (cfg.xtype.match(/^Form$/)) {
40162             
40163             var el;
40164             //if (this.footer) {
40165             //    el = this.footer.container.insertSibling(false, 'before');
40166             //} else {
40167                 el = this.el.createChild();
40168             //}
40169
40170             this.form = new  Roo.form.Form(cfg);
40171             
40172             
40173             if ( this.form.allItems.length) {
40174                 this.form.render(el.dom);
40175             }
40176             return this.form;
40177         }
40178         // should only have one of theses..
40179         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40180             // views.. should not be just added - used named prop 'view''
40181             
40182             cfg.el = this.el.appendChild(document.createElement("div"));
40183             // factory?
40184             
40185             var ret = new Roo.factory(cfg);
40186              
40187              ret.render && ret.render(false, ''); // render blank..
40188             this.view = ret;
40189             return ret;
40190         }
40191         return false;
40192     }
40193     \*/
40194 });
40195  
40196 /**
40197  * @class Roo.bootstrap.panel.Grid
40198  * @extends Roo.bootstrap.panel.Content
40199  * @constructor
40200  * Create a new GridPanel.
40201  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40202  * @param {Object} config A the config object
40203   
40204  */
40205
40206
40207
40208 Roo.bootstrap.panel.Grid = function(config)
40209 {
40210     
40211       
40212     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40213         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40214
40215     config.el = this.wrapper;
40216     //this.el = this.wrapper;
40217     
40218       if (config.container) {
40219         // ctor'ed from a Border/panel.grid
40220         
40221         
40222         this.wrapper.setStyle("overflow", "hidden");
40223         this.wrapper.addClass('roo-grid-container');
40224
40225     }
40226     
40227     
40228     if(config.toolbar){
40229         var tool_el = this.wrapper.createChild();    
40230         this.toolbar = Roo.factory(config.toolbar);
40231         var ti = [];
40232         if (config.toolbar.items) {
40233             ti = config.toolbar.items ;
40234             delete config.toolbar.items ;
40235         }
40236         
40237         var nitems = [];
40238         this.toolbar.render(tool_el);
40239         for(var i =0;i < ti.length;i++) {
40240           //  Roo.log(['add child', items[i]]);
40241             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40242         }
40243         this.toolbar.items = nitems;
40244         
40245         delete config.toolbar;
40246     }
40247     
40248     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40249     config.grid.scrollBody = true;;
40250     config.grid.monitorWindowResize = false; // turn off autosizing
40251     config.grid.autoHeight = false;
40252     config.grid.autoWidth = false;
40253     
40254     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40255     
40256     if (config.background) {
40257         // render grid on panel activation (if panel background)
40258         this.on('activate', function(gp) {
40259             if (!gp.grid.rendered) {
40260                 gp.grid.render(this.wrapper);
40261                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40262             }
40263         });
40264             
40265     } else {
40266         this.grid.render(this.wrapper);
40267         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40268
40269     }
40270     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40271     // ??? needed ??? config.el = this.wrapper;
40272     
40273     
40274     
40275   
40276     // xtype created footer. - not sure if will work as we normally have to render first..
40277     if (this.footer && !this.footer.el && this.footer.xtype) {
40278         
40279         var ctr = this.grid.getView().getFooterPanel(true);
40280         this.footer.dataSource = this.grid.dataSource;
40281         this.footer = Roo.factory(this.footer, Roo);
40282         this.footer.render(ctr);
40283         
40284     }
40285     
40286     
40287     
40288     
40289      
40290 };
40291
40292 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40293     getId : function(){
40294         return this.grid.id;
40295     },
40296     
40297     /**
40298      * Returns the grid for this panel
40299      * @return {Roo.bootstrap.Table} 
40300      */
40301     getGrid : function(){
40302         return this.grid;    
40303     },
40304     
40305     setSize : function(width, height){
40306         if(!this.ignoreResize(width, height)){
40307             var grid = this.grid;
40308             var size = this.adjustForComponents(width, height);
40309             // tfoot is not a footer?
40310           
40311             
40312             var gridel = grid.getGridEl();
40313             gridel.setSize(size.width, size.height);
40314             
40315             var tbd = grid.getGridEl().select('tbody', true).first();
40316             var thd = grid.getGridEl().select('thead',true).first();
40317             var tbf= grid.getGridEl().select('tfoot', true).first();
40318
40319             if (tbf) {
40320                 size.height -= tbf.getHeight();
40321             }
40322             if (thd) {
40323                 size.height -= thd.getHeight();
40324             }
40325             
40326             tbd.setSize(size.width, size.height );
40327             // this is for the account management tab -seems to work there.
40328             var thd = grid.getGridEl().select('thead',true).first();
40329             //if (tbd) {
40330             //    tbd.setSize(size.width, size.height - thd.getHeight());
40331             //}
40332              
40333             grid.autoSize();
40334         }
40335     },
40336      
40337     
40338     
40339     beforeSlide : function(){
40340         this.grid.getView().scroller.clip();
40341     },
40342     
40343     afterSlide : function(){
40344         this.grid.getView().scroller.unclip();
40345     },
40346     
40347     destroy : function(){
40348         this.grid.destroy();
40349         delete this.grid;
40350         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40351     }
40352 });
40353
40354 /**
40355  * @class Roo.bootstrap.panel.Nest
40356  * @extends Roo.bootstrap.panel.Content
40357  * @constructor
40358  * Create a new Panel, that can contain a layout.Border.
40359  * 
40360  * 
40361  * @param {Roo.BorderLayout} layout The layout for this panel
40362  * @param {String/Object} config A string to set only the title or a config object
40363  */
40364 Roo.bootstrap.panel.Nest = function(config)
40365 {
40366     // construct with only one argument..
40367     /* FIXME - implement nicer consturctors
40368     if (layout.layout) {
40369         config = layout;
40370         layout = config.layout;
40371         delete config.layout;
40372     }
40373     if (layout.xtype && !layout.getEl) {
40374         // then layout needs constructing..
40375         layout = Roo.factory(layout, Roo);
40376     }
40377     */
40378     
40379     config.el =  config.layout.getEl();
40380     
40381     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40382     
40383     config.layout.monitorWindowResize = false; // turn off autosizing
40384     this.layout = config.layout;
40385     this.layout.getEl().addClass("roo-layout-nested-layout");
40386     this.layout.parent = this;
40387     
40388     
40389     
40390     
40391 };
40392
40393 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40394
40395     setSize : function(width, height){
40396         if(!this.ignoreResize(width, height)){
40397             var size = this.adjustForComponents(width, height);
40398             var el = this.layout.getEl();
40399             if (size.height < 1) {
40400                 el.setWidth(size.width);   
40401             } else {
40402                 el.setSize(size.width, size.height);
40403             }
40404             var touch = el.dom.offsetWidth;
40405             this.layout.layout();
40406             // ie requires a double layout on the first pass
40407             if(Roo.isIE && !this.initialized){
40408                 this.initialized = true;
40409                 this.layout.layout();
40410             }
40411         }
40412     },
40413     
40414     // activate all subpanels if not currently active..
40415     
40416     setActiveState : function(active){
40417         this.active = active;
40418         this.setActiveClass(active);
40419         
40420         if(!active){
40421             this.fireEvent("deactivate", this);
40422             return;
40423         }
40424         
40425         this.fireEvent("activate", this);
40426         // not sure if this should happen before or after..
40427         if (!this.layout) {
40428             return; // should not happen..
40429         }
40430         var reg = false;
40431         for (var r in this.layout.regions) {
40432             reg = this.layout.getRegion(r);
40433             if (reg.getActivePanel()) {
40434                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40435                 reg.setActivePanel(reg.getActivePanel());
40436                 continue;
40437             }
40438             if (!reg.panels.length) {
40439                 continue;
40440             }
40441             reg.showPanel(reg.getPanel(0));
40442         }
40443         
40444         
40445         
40446         
40447     },
40448     
40449     /**
40450      * Returns the nested BorderLayout for this panel
40451      * @return {Roo.BorderLayout} 
40452      */
40453     getLayout : function(){
40454         return this.layout;
40455     },
40456     
40457      /**
40458      * Adds a xtype elements to the layout of the nested panel
40459      * <pre><code>
40460
40461 panel.addxtype({
40462        xtype : 'ContentPanel',
40463        region: 'west',
40464        items: [ .... ]
40465    }
40466 );
40467
40468 panel.addxtype({
40469         xtype : 'NestedLayoutPanel',
40470         region: 'west',
40471         layout: {
40472            center: { },
40473            west: { }   
40474         },
40475         items : [ ... list of content panels or nested layout panels.. ]
40476    }
40477 );
40478 </code></pre>
40479      * @param {Object} cfg Xtype definition of item to add.
40480      */
40481     addxtype : function(cfg) {
40482         return this.layout.addxtype(cfg);
40483     
40484     }
40485 });/*
40486  * Based on:
40487  * Ext JS Library 1.1.1
40488  * Copyright(c) 2006-2007, Ext JS, LLC.
40489  *
40490  * Originally Released Under LGPL - original licence link has changed is not relivant.
40491  *
40492  * Fork - LGPL
40493  * <script type="text/javascript">
40494  */
40495 /**
40496  * @class Roo.TabPanel
40497  * @extends Roo.util.Observable
40498  * A lightweight tab container.
40499  * <br><br>
40500  * Usage:
40501  * <pre><code>
40502 // basic tabs 1, built from existing content
40503 var tabs = new Roo.TabPanel("tabs1");
40504 tabs.addTab("script", "View Script");
40505 tabs.addTab("markup", "View Markup");
40506 tabs.activate("script");
40507
40508 // more advanced tabs, built from javascript
40509 var jtabs = new Roo.TabPanel("jtabs");
40510 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40511
40512 // set up the UpdateManager
40513 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40514 var updater = tab2.getUpdateManager();
40515 updater.setDefaultUrl("ajax1.htm");
40516 tab2.on('activate', updater.refresh, updater, true);
40517
40518 // Use setUrl for Ajax loading
40519 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40520 tab3.setUrl("ajax2.htm", null, true);
40521
40522 // Disabled tab
40523 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40524 tab4.disable();
40525
40526 jtabs.activate("jtabs-1");
40527  * </code></pre>
40528  * @constructor
40529  * Create a new TabPanel.
40530  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40531  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40532  */
40533 Roo.bootstrap.panel.Tabs = function(config){
40534     /**
40535     * The container element for this TabPanel.
40536     * @type Roo.Element
40537     */
40538     this.el = Roo.get(config.el);
40539     delete config.el;
40540     if(config){
40541         if(typeof config == "boolean"){
40542             this.tabPosition = config ? "bottom" : "top";
40543         }else{
40544             Roo.apply(this, config);
40545         }
40546     }
40547     
40548     if(this.tabPosition == "bottom"){
40549         // if tabs are at the bottom = create the body first.
40550         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40551         this.el.addClass("roo-tabs-bottom");
40552     }
40553     // next create the tabs holders
40554     
40555     if (this.tabPosition == "west"){
40556         
40557         var reg = this.region; // fake it..
40558         while (reg) {
40559             if (!reg.mgr.parent) {
40560                 break;
40561             }
40562             reg = reg.mgr.parent.region;
40563         }
40564         Roo.log("got nest?");
40565         Roo.log(reg);
40566         if (reg.mgr.getRegion('west')) {
40567             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40568             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40569             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40570             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40571             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40572         
40573             
40574         }
40575         
40576         
40577     } else {
40578      
40579         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40580         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40581         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40582         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40583     }
40584     
40585     
40586     if(Roo.isIE){
40587         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40588     }
40589     
40590     // finally - if tabs are at the top, then create the body last..
40591     if(this.tabPosition != "bottom"){
40592         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40593          * @type Roo.Element
40594          */
40595         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40596         this.el.addClass("roo-tabs-top");
40597     }
40598     this.items = [];
40599
40600     this.bodyEl.setStyle("position", "relative");
40601
40602     this.active = null;
40603     this.activateDelegate = this.activate.createDelegate(this);
40604
40605     this.addEvents({
40606         /**
40607          * @event tabchange
40608          * Fires when the active tab changes
40609          * @param {Roo.TabPanel} this
40610          * @param {Roo.TabPanelItem} activePanel The new active tab
40611          */
40612         "tabchange": true,
40613         /**
40614          * @event beforetabchange
40615          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40616          * @param {Roo.TabPanel} this
40617          * @param {Object} e Set cancel to true on this object to cancel the tab change
40618          * @param {Roo.TabPanelItem} tab The tab being changed to
40619          */
40620         "beforetabchange" : true
40621     });
40622
40623     Roo.EventManager.onWindowResize(this.onResize, this);
40624     this.cpad = this.el.getPadding("lr");
40625     this.hiddenCount = 0;
40626
40627
40628     // toolbar on the tabbar support...
40629     if (this.toolbar) {
40630         alert("no toolbar support yet");
40631         this.toolbar  = false;
40632         /*
40633         var tcfg = this.toolbar;
40634         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40635         this.toolbar = new Roo.Toolbar(tcfg);
40636         if (Roo.isSafari) {
40637             var tbl = tcfg.container.child('table', true);
40638             tbl.setAttribute('width', '100%');
40639         }
40640         */
40641         
40642     }
40643    
40644
40645
40646     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40647 };
40648
40649 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40650     /*
40651      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40652      */
40653     tabPosition : "top",
40654     /*
40655      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40656      */
40657     currentTabWidth : 0,
40658     /*
40659      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40660      */
40661     minTabWidth : 40,
40662     /*
40663      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40664      */
40665     maxTabWidth : 250,
40666     /*
40667      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40668      */
40669     preferredTabWidth : 175,
40670     /*
40671      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40672      */
40673     resizeTabs : false,
40674     /*
40675      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40676      */
40677     monitorResize : true,
40678     /*
40679      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40680      */
40681     toolbar : false,  // set by caller..
40682     
40683     region : false, /// set by caller
40684     
40685     disableTooltips : true, // not used yet...
40686
40687     /**
40688      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40689      * @param {String} id The id of the div to use <b>or create</b>
40690      * @param {String} text The text for the tab
40691      * @param {String} content (optional) Content to put in the TabPanelItem body
40692      * @param {Boolean} closable (optional) True to create a close icon on the tab
40693      * @return {Roo.TabPanelItem} The created TabPanelItem
40694      */
40695     addTab : function(id, text, content, closable, tpl)
40696     {
40697         var item = new Roo.bootstrap.panel.TabItem({
40698             panel: this,
40699             id : id,
40700             text : text,
40701             closable : closable,
40702             tpl : tpl
40703         });
40704         this.addTabItem(item);
40705         if(content){
40706             item.setContent(content);
40707         }
40708         return item;
40709     },
40710
40711     /**
40712      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40713      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40714      * @return {Roo.TabPanelItem}
40715      */
40716     getTab : function(id){
40717         return this.items[id];
40718     },
40719
40720     /**
40721      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40722      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40723      */
40724     hideTab : function(id){
40725         var t = this.items[id];
40726         if(!t.isHidden()){
40727            t.setHidden(true);
40728            this.hiddenCount++;
40729            this.autoSizeTabs();
40730         }
40731     },
40732
40733     /**
40734      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40735      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40736      */
40737     unhideTab : function(id){
40738         var t = this.items[id];
40739         if(t.isHidden()){
40740            t.setHidden(false);
40741            this.hiddenCount--;
40742            this.autoSizeTabs();
40743         }
40744     },
40745
40746     /**
40747      * Adds an existing {@link Roo.TabPanelItem}.
40748      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40749      */
40750     addTabItem : function(item)
40751     {
40752         this.items[item.id] = item;
40753         this.items.push(item);
40754         this.autoSizeTabs();
40755       //  if(this.resizeTabs){
40756     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40757   //         this.autoSizeTabs();
40758 //        }else{
40759 //            item.autoSize();
40760        // }
40761     },
40762
40763     /**
40764      * Removes a {@link Roo.TabPanelItem}.
40765      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40766      */
40767     removeTab : function(id){
40768         var items = this.items;
40769         var tab = items[id];
40770         if(!tab) { return; }
40771         var index = items.indexOf(tab);
40772         if(this.active == tab && items.length > 1){
40773             var newTab = this.getNextAvailable(index);
40774             if(newTab) {
40775                 newTab.activate();
40776             }
40777         }
40778         this.stripEl.dom.removeChild(tab.pnode.dom);
40779         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40780             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40781         }
40782         items.splice(index, 1);
40783         delete this.items[tab.id];
40784         tab.fireEvent("close", tab);
40785         tab.purgeListeners();
40786         this.autoSizeTabs();
40787     },
40788
40789     getNextAvailable : function(start){
40790         var items = this.items;
40791         var index = start;
40792         // look for a next tab that will slide over to
40793         // replace the one being removed
40794         while(index < items.length){
40795             var item = items[++index];
40796             if(item && !item.isHidden()){
40797                 return item;
40798             }
40799         }
40800         // if one isn't found select the previous tab (on the left)
40801         index = start;
40802         while(index >= 0){
40803             var item = items[--index];
40804             if(item && !item.isHidden()){
40805                 return item;
40806             }
40807         }
40808         return null;
40809     },
40810
40811     /**
40812      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40813      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40814      */
40815     disableTab : function(id){
40816         var tab = this.items[id];
40817         if(tab && this.active != tab){
40818             tab.disable();
40819         }
40820     },
40821
40822     /**
40823      * Enables a {@link Roo.TabPanelItem} that is disabled.
40824      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40825      */
40826     enableTab : function(id){
40827         var tab = this.items[id];
40828         tab.enable();
40829     },
40830
40831     /**
40832      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40833      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40834      * @return {Roo.TabPanelItem} The TabPanelItem.
40835      */
40836     activate : function(id)
40837     {
40838         //Roo.log('activite:'  + id);
40839         
40840         var tab = this.items[id];
40841         if(!tab){
40842             return null;
40843         }
40844         if(tab == this.active || tab.disabled){
40845             return tab;
40846         }
40847         var e = {};
40848         this.fireEvent("beforetabchange", this, e, tab);
40849         if(e.cancel !== true && !tab.disabled){
40850             if(this.active){
40851                 this.active.hide();
40852             }
40853             this.active = this.items[id];
40854             this.active.show();
40855             this.fireEvent("tabchange", this, this.active);
40856         }
40857         return tab;
40858     },
40859
40860     /**
40861      * Gets the active {@link Roo.TabPanelItem}.
40862      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40863      */
40864     getActiveTab : function(){
40865         return this.active;
40866     },
40867
40868     /**
40869      * Updates the tab body element to fit the height of the container element
40870      * for overflow scrolling
40871      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40872      */
40873     syncHeight : function(targetHeight){
40874         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40875         var bm = this.bodyEl.getMargins();
40876         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40877         this.bodyEl.setHeight(newHeight);
40878         return newHeight;
40879     },
40880
40881     onResize : function(){
40882         if(this.monitorResize){
40883             this.autoSizeTabs();
40884         }
40885     },
40886
40887     /**
40888      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40889      */
40890     beginUpdate : function(){
40891         this.updating = true;
40892     },
40893
40894     /**
40895      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40896      */
40897     endUpdate : function(){
40898         this.updating = false;
40899         this.autoSizeTabs();
40900     },
40901
40902     /**
40903      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40904      */
40905     autoSizeTabs : function()
40906     {
40907         var count = this.items.length;
40908         var vcount = count - this.hiddenCount;
40909         
40910         if (vcount < 2) {
40911             this.stripEl.hide();
40912         } else {
40913             this.stripEl.show();
40914         }
40915         
40916         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40917             return;
40918         }
40919         
40920         
40921         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40922         var availWidth = Math.floor(w / vcount);
40923         var b = this.stripBody;
40924         if(b.getWidth() > w){
40925             var tabs = this.items;
40926             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40927             if(availWidth < this.minTabWidth){
40928                 /*if(!this.sleft){    // incomplete scrolling code
40929                     this.createScrollButtons();
40930                 }
40931                 this.showScroll();
40932                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40933             }
40934         }else{
40935             if(this.currentTabWidth < this.preferredTabWidth){
40936                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40937             }
40938         }
40939     },
40940
40941     /**
40942      * Returns the number of tabs in this TabPanel.
40943      * @return {Number}
40944      */
40945      getCount : function(){
40946          return this.items.length;
40947      },
40948
40949     /**
40950      * Resizes all the tabs to the passed width
40951      * @param {Number} The new width
40952      */
40953     setTabWidth : function(width){
40954         this.currentTabWidth = width;
40955         for(var i = 0, len = this.items.length; i < len; i++) {
40956                 if(!this.items[i].isHidden()) {
40957                 this.items[i].setWidth(width);
40958             }
40959         }
40960     },
40961
40962     /**
40963      * Destroys this TabPanel
40964      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40965      */
40966     destroy : function(removeEl){
40967         Roo.EventManager.removeResizeListener(this.onResize, this);
40968         for(var i = 0, len = this.items.length; i < len; i++){
40969             this.items[i].purgeListeners();
40970         }
40971         if(removeEl === true){
40972             this.el.update("");
40973             this.el.remove();
40974         }
40975     },
40976     
40977     createStrip : function(container)
40978     {
40979         var strip = document.createElement("nav");
40980         strip.className = Roo.bootstrap.version == 4 ?
40981             "navbar-light bg-light" : 
40982             "navbar navbar-default"; //"x-tabs-wrap";
40983         container.appendChild(strip);
40984         return strip;
40985     },
40986     
40987     createStripList : function(strip)
40988     {
40989         // div wrapper for retard IE
40990         // returns the "tr" element.
40991         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40992         //'<div class="x-tabs-strip-wrap">'+
40993           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40994           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40995         return strip.firstChild; //.firstChild.firstChild.firstChild;
40996     },
40997     createBody : function(container)
40998     {
40999         var body = document.createElement("div");
41000         Roo.id(body, "tab-body");
41001         //Roo.fly(body).addClass("x-tabs-body");
41002         Roo.fly(body).addClass("tab-content");
41003         container.appendChild(body);
41004         return body;
41005     },
41006     createItemBody :function(bodyEl, id){
41007         var body = Roo.getDom(id);
41008         if(!body){
41009             body = document.createElement("div");
41010             body.id = id;
41011         }
41012         //Roo.fly(body).addClass("x-tabs-item-body");
41013         Roo.fly(body).addClass("tab-pane");
41014          bodyEl.insertBefore(body, bodyEl.firstChild);
41015         return body;
41016     },
41017     /** @private */
41018     createStripElements :  function(stripEl, text, closable, tpl)
41019     {
41020         var td = document.createElement("li"); // was td..
41021         td.className = 'nav-item';
41022         
41023         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41024         
41025         
41026         stripEl.appendChild(td);
41027         /*if(closable){
41028             td.className = "x-tabs-closable";
41029             if(!this.closeTpl){
41030                 this.closeTpl = new Roo.Template(
41031                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41032                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41033                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41034                 );
41035             }
41036             var el = this.closeTpl.overwrite(td, {"text": text});
41037             var close = el.getElementsByTagName("div")[0];
41038             var inner = el.getElementsByTagName("em")[0];
41039             return {"el": el, "close": close, "inner": inner};
41040         } else {
41041         */
41042         // not sure what this is..
41043 //            if(!this.tabTpl){
41044                 //this.tabTpl = new Roo.Template(
41045                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41046                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41047                 //);
41048 //                this.tabTpl = new Roo.Template(
41049 //                   '<a href="#">' +
41050 //                   '<span unselectable="on"' +
41051 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41052 //                            ' >{text}</span></a>'
41053 //                );
41054 //                
41055 //            }
41056
41057
41058             var template = tpl || this.tabTpl || false;
41059             
41060             if(!template){
41061                 template =  new Roo.Template(
41062                         Roo.bootstrap.version == 4 ? 
41063                             (
41064                                 '<a class="nav-link" href="#" unselectable="on"' +
41065                                      (this.disableTooltips ? '' : ' title="{text}"') +
41066                                      ' >{text}</a>'
41067                             ) : (
41068                                 '<a class="nav-link" href="#">' +
41069                                 '<span unselectable="on"' +
41070                                          (this.disableTooltips ? '' : ' title="{text}"') +
41071                                     ' >{text}</span></a>'
41072                             )
41073                 );
41074             }
41075             
41076             switch (typeof(template)) {
41077                 case 'object' :
41078                     break;
41079                 case 'string' :
41080                     template = new Roo.Template(template);
41081                     break;
41082                 default :
41083                     break;
41084             }
41085             
41086             var el = template.overwrite(td, {"text": text});
41087             
41088             var inner = el.getElementsByTagName("span")[0];
41089             
41090             return {"el": el, "inner": inner};
41091             
41092     }
41093         
41094     
41095 });
41096
41097 /**
41098  * @class Roo.TabPanelItem
41099  * @extends Roo.util.Observable
41100  * Represents an individual item (tab plus body) in a TabPanel.
41101  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41102  * @param {String} id The id of this TabPanelItem
41103  * @param {String} text The text for the tab of this TabPanelItem
41104  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41105  */
41106 Roo.bootstrap.panel.TabItem = function(config){
41107     /**
41108      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41109      * @type Roo.TabPanel
41110      */
41111     this.tabPanel = config.panel;
41112     /**
41113      * The id for this TabPanelItem
41114      * @type String
41115      */
41116     this.id = config.id;
41117     /** @private */
41118     this.disabled = false;
41119     /** @private */
41120     this.text = config.text;
41121     /** @private */
41122     this.loaded = false;
41123     this.closable = config.closable;
41124
41125     /**
41126      * The body element for this TabPanelItem.
41127      * @type Roo.Element
41128      */
41129     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41130     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41131     this.bodyEl.setStyle("display", "block");
41132     this.bodyEl.setStyle("zoom", "1");
41133     //this.hideAction();
41134
41135     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41136     /** @private */
41137     this.el = Roo.get(els.el);
41138     this.inner = Roo.get(els.inner, true);
41139      this.textEl = Roo.bootstrap.version == 4 ?
41140         this.el : Roo.get(this.el.dom.firstChild, true);
41141
41142     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41143     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41144
41145     
41146 //    this.el.on("mousedown", this.onTabMouseDown, this);
41147     this.el.on("click", this.onTabClick, this);
41148     /** @private */
41149     if(config.closable){
41150         var c = Roo.get(els.close, true);
41151         c.dom.title = this.closeText;
41152         c.addClassOnOver("close-over");
41153         c.on("click", this.closeClick, this);
41154      }
41155
41156     this.addEvents({
41157          /**
41158          * @event activate
41159          * Fires when this tab becomes the active tab.
41160          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41161          * @param {Roo.TabPanelItem} this
41162          */
41163         "activate": true,
41164         /**
41165          * @event beforeclose
41166          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41167          * @param {Roo.TabPanelItem} this
41168          * @param {Object} e Set cancel to true on this object to cancel the close.
41169          */
41170         "beforeclose": true,
41171         /**
41172          * @event close
41173          * Fires when this tab is closed.
41174          * @param {Roo.TabPanelItem} this
41175          */
41176          "close": true,
41177         /**
41178          * @event deactivate
41179          * Fires when this tab is no longer the active tab.
41180          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41181          * @param {Roo.TabPanelItem} this
41182          */
41183          "deactivate" : true
41184     });
41185     this.hidden = false;
41186
41187     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41188 };
41189
41190 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41191            {
41192     purgeListeners : function(){
41193        Roo.util.Observable.prototype.purgeListeners.call(this);
41194        this.el.removeAllListeners();
41195     },
41196     /**
41197      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41198      */
41199     show : function(){
41200         this.status_node.addClass("active");
41201         this.showAction();
41202         if(Roo.isOpera){
41203             this.tabPanel.stripWrap.repaint();
41204         }
41205         this.fireEvent("activate", this.tabPanel, this);
41206     },
41207
41208     /**
41209      * Returns true if this tab is the active tab.
41210      * @return {Boolean}
41211      */
41212     isActive : function(){
41213         return this.tabPanel.getActiveTab() == this;
41214     },
41215
41216     /**
41217      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41218      */
41219     hide : function(){
41220         this.status_node.removeClass("active");
41221         this.hideAction();
41222         this.fireEvent("deactivate", this.tabPanel, this);
41223     },
41224
41225     hideAction : function(){
41226         this.bodyEl.hide();
41227         this.bodyEl.setStyle("position", "absolute");
41228         this.bodyEl.setLeft("-20000px");
41229         this.bodyEl.setTop("-20000px");
41230     },
41231
41232     showAction : function(){
41233         this.bodyEl.setStyle("position", "relative");
41234         this.bodyEl.setTop("");
41235         this.bodyEl.setLeft("");
41236         this.bodyEl.show();
41237     },
41238
41239     /**
41240      * Set the tooltip for the tab.
41241      * @param {String} tooltip The tab's tooltip
41242      */
41243     setTooltip : function(text){
41244         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41245             this.textEl.dom.qtip = text;
41246             this.textEl.dom.removeAttribute('title');
41247         }else{
41248             this.textEl.dom.title = text;
41249         }
41250     },
41251
41252     onTabClick : function(e){
41253         e.preventDefault();
41254         this.tabPanel.activate(this.id);
41255     },
41256
41257     onTabMouseDown : function(e){
41258         e.preventDefault();
41259         this.tabPanel.activate(this.id);
41260     },
41261 /*
41262     getWidth : function(){
41263         return this.inner.getWidth();
41264     },
41265
41266     setWidth : function(width){
41267         var iwidth = width - this.linode.getPadding("lr");
41268         this.inner.setWidth(iwidth);
41269         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41270         this.linode.setWidth(width);
41271     },
41272 */
41273     /**
41274      * Show or hide the tab
41275      * @param {Boolean} hidden True to hide or false to show.
41276      */
41277     setHidden : function(hidden){
41278         this.hidden = hidden;
41279         this.linode.setStyle("display", hidden ? "none" : "");
41280     },
41281
41282     /**
41283      * Returns true if this tab is "hidden"
41284      * @return {Boolean}
41285      */
41286     isHidden : function(){
41287         return this.hidden;
41288     },
41289
41290     /**
41291      * Returns the text for this tab
41292      * @return {String}
41293      */
41294     getText : function(){
41295         return this.text;
41296     },
41297     /*
41298     autoSize : function(){
41299         //this.el.beginMeasure();
41300         this.textEl.setWidth(1);
41301         /*
41302          *  #2804 [new] Tabs in Roojs
41303          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41304          */
41305         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41306         //this.el.endMeasure();
41307     //},
41308
41309     /**
41310      * Sets the text for the tab (Note: this also sets the tooltip text)
41311      * @param {String} text The tab's text and tooltip
41312      */
41313     setText : function(text){
41314         this.text = text;
41315         this.textEl.update(text);
41316         this.setTooltip(text);
41317         //if(!this.tabPanel.resizeTabs){
41318         //    this.autoSize();
41319         //}
41320     },
41321     /**
41322      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41323      */
41324     activate : function(){
41325         this.tabPanel.activate(this.id);
41326     },
41327
41328     /**
41329      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41330      */
41331     disable : function(){
41332         if(this.tabPanel.active != this){
41333             this.disabled = true;
41334             this.status_node.addClass("disabled");
41335         }
41336     },
41337
41338     /**
41339      * Enables this TabPanelItem if it was previously disabled.
41340      */
41341     enable : function(){
41342         this.disabled = false;
41343         this.status_node.removeClass("disabled");
41344     },
41345
41346     /**
41347      * Sets the content for this TabPanelItem.
41348      * @param {String} content The content
41349      * @param {Boolean} loadScripts true to look for and load scripts
41350      */
41351     setContent : function(content, loadScripts){
41352         this.bodyEl.update(content, loadScripts);
41353     },
41354
41355     /**
41356      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41357      * @return {Roo.UpdateManager} The UpdateManager
41358      */
41359     getUpdateManager : function(){
41360         return this.bodyEl.getUpdateManager();
41361     },
41362
41363     /**
41364      * Set a URL to be used to load the content for this TabPanelItem.
41365      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41366      * @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)
41367      * @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)
41368      * @return {Roo.UpdateManager} The UpdateManager
41369      */
41370     setUrl : function(url, params, loadOnce){
41371         if(this.refreshDelegate){
41372             this.un('activate', this.refreshDelegate);
41373         }
41374         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41375         this.on("activate", this.refreshDelegate);
41376         return this.bodyEl.getUpdateManager();
41377     },
41378
41379     /** @private */
41380     _handleRefresh : function(url, params, loadOnce){
41381         if(!loadOnce || !this.loaded){
41382             var updater = this.bodyEl.getUpdateManager();
41383             updater.update(url, params, this._setLoaded.createDelegate(this));
41384         }
41385     },
41386
41387     /**
41388      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41389      *   Will fail silently if the setUrl method has not been called.
41390      *   This does not activate the panel, just updates its content.
41391      */
41392     refresh : function(){
41393         if(this.refreshDelegate){
41394            this.loaded = false;
41395            this.refreshDelegate();
41396         }
41397     },
41398
41399     /** @private */
41400     _setLoaded : function(){
41401         this.loaded = true;
41402     },
41403
41404     /** @private */
41405     closeClick : function(e){
41406         var o = {};
41407         e.stopEvent();
41408         this.fireEvent("beforeclose", this, o);
41409         if(o.cancel !== true){
41410             this.tabPanel.removeTab(this.id);
41411         }
41412     },
41413     /**
41414      * The text displayed in the tooltip for the close icon.
41415      * @type String
41416      */
41417     closeText : "Close this tab"
41418 });
41419 /**
41420 *    This script refer to:
41421 *    Title: International Telephone Input
41422 *    Author: Jack O'Connor
41423 *    Code version:  v12.1.12
41424 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41425 **/
41426
41427 Roo.bootstrap.PhoneInputData = function() {
41428     var d = [
41429       [
41430         "Afghanistan (‫افغانستان‬‎)",
41431         "af",
41432         "93"
41433       ],
41434       [
41435         "Albania (Shqipëri)",
41436         "al",
41437         "355"
41438       ],
41439       [
41440         "Algeria (‫الجزائر‬‎)",
41441         "dz",
41442         "213"
41443       ],
41444       [
41445         "American Samoa",
41446         "as",
41447         "1684"
41448       ],
41449       [
41450         "Andorra",
41451         "ad",
41452         "376"
41453       ],
41454       [
41455         "Angola",
41456         "ao",
41457         "244"
41458       ],
41459       [
41460         "Anguilla",
41461         "ai",
41462         "1264"
41463       ],
41464       [
41465         "Antigua and Barbuda",
41466         "ag",
41467         "1268"
41468       ],
41469       [
41470         "Argentina",
41471         "ar",
41472         "54"
41473       ],
41474       [
41475         "Armenia (Հայաստան)",
41476         "am",
41477         "374"
41478       ],
41479       [
41480         "Aruba",
41481         "aw",
41482         "297"
41483       ],
41484       [
41485         "Australia",
41486         "au",
41487         "61",
41488         0
41489       ],
41490       [
41491         "Austria (Österreich)",
41492         "at",
41493         "43"
41494       ],
41495       [
41496         "Azerbaijan (Azərbaycan)",
41497         "az",
41498         "994"
41499       ],
41500       [
41501         "Bahamas",
41502         "bs",
41503         "1242"
41504       ],
41505       [
41506         "Bahrain (‫البحرين‬‎)",
41507         "bh",
41508         "973"
41509       ],
41510       [
41511         "Bangladesh (বাংলাদেশ)",
41512         "bd",
41513         "880"
41514       ],
41515       [
41516         "Barbados",
41517         "bb",
41518         "1246"
41519       ],
41520       [
41521         "Belarus (Беларусь)",
41522         "by",
41523         "375"
41524       ],
41525       [
41526         "Belgium (België)",
41527         "be",
41528         "32"
41529       ],
41530       [
41531         "Belize",
41532         "bz",
41533         "501"
41534       ],
41535       [
41536         "Benin (Bénin)",
41537         "bj",
41538         "229"
41539       ],
41540       [
41541         "Bermuda",
41542         "bm",
41543         "1441"
41544       ],
41545       [
41546         "Bhutan (འབྲུག)",
41547         "bt",
41548         "975"
41549       ],
41550       [
41551         "Bolivia",
41552         "bo",
41553         "591"
41554       ],
41555       [
41556         "Bosnia and Herzegovina (Босна и Херцеговина)",
41557         "ba",
41558         "387"
41559       ],
41560       [
41561         "Botswana",
41562         "bw",
41563         "267"
41564       ],
41565       [
41566         "Brazil (Brasil)",
41567         "br",
41568         "55"
41569       ],
41570       [
41571         "British Indian Ocean Territory",
41572         "io",
41573         "246"
41574       ],
41575       [
41576         "British Virgin Islands",
41577         "vg",
41578         "1284"
41579       ],
41580       [
41581         "Brunei",
41582         "bn",
41583         "673"
41584       ],
41585       [
41586         "Bulgaria (България)",
41587         "bg",
41588         "359"
41589       ],
41590       [
41591         "Burkina Faso",
41592         "bf",
41593         "226"
41594       ],
41595       [
41596         "Burundi (Uburundi)",
41597         "bi",
41598         "257"
41599       ],
41600       [
41601         "Cambodia (កម្ពុជា)",
41602         "kh",
41603         "855"
41604       ],
41605       [
41606         "Cameroon (Cameroun)",
41607         "cm",
41608         "237"
41609       ],
41610       [
41611         "Canada",
41612         "ca",
41613         "1",
41614         1,
41615         ["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"]
41616       ],
41617       [
41618         "Cape Verde (Kabu Verdi)",
41619         "cv",
41620         "238"
41621       ],
41622       [
41623         "Caribbean Netherlands",
41624         "bq",
41625         "599",
41626         1
41627       ],
41628       [
41629         "Cayman Islands",
41630         "ky",
41631         "1345"
41632       ],
41633       [
41634         "Central African Republic (République centrafricaine)",
41635         "cf",
41636         "236"
41637       ],
41638       [
41639         "Chad (Tchad)",
41640         "td",
41641         "235"
41642       ],
41643       [
41644         "Chile",
41645         "cl",
41646         "56"
41647       ],
41648       [
41649         "China (中国)",
41650         "cn",
41651         "86"
41652       ],
41653       [
41654         "Christmas Island",
41655         "cx",
41656         "61",
41657         2
41658       ],
41659       [
41660         "Cocos (Keeling) Islands",
41661         "cc",
41662         "61",
41663         1
41664       ],
41665       [
41666         "Colombia",
41667         "co",
41668         "57"
41669       ],
41670       [
41671         "Comoros (‫جزر القمر‬‎)",
41672         "km",
41673         "269"
41674       ],
41675       [
41676         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41677         "cd",
41678         "243"
41679       ],
41680       [
41681         "Congo (Republic) (Congo-Brazzaville)",
41682         "cg",
41683         "242"
41684       ],
41685       [
41686         "Cook Islands",
41687         "ck",
41688         "682"
41689       ],
41690       [
41691         "Costa Rica",
41692         "cr",
41693         "506"
41694       ],
41695       [
41696         "Côte d’Ivoire",
41697         "ci",
41698         "225"
41699       ],
41700       [
41701         "Croatia (Hrvatska)",
41702         "hr",
41703         "385"
41704       ],
41705       [
41706         "Cuba",
41707         "cu",
41708         "53"
41709       ],
41710       [
41711         "Curaçao",
41712         "cw",
41713         "599",
41714         0
41715       ],
41716       [
41717         "Cyprus (Κύπρος)",
41718         "cy",
41719         "357"
41720       ],
41721       [
41722         "Czech Republic (Česká republika)",
41723         "cz",
41724         "420"
41725       ],
41726       [
41727         "Denmark (Danmark)",
41728         "dk",
41729         "45"
41730       ],
41731       [
41732         "Djibouti",
41733         "dj",
41734         "253"
41735       ],
41736       [
41737         "Dominica",
41738         "dm",
41739         "1767"
41740       ],
41741       [
41742         "Dominican Republic (República Dominicana)",
41743         "do",
41744         "1",
41745         2,
41746         ["809", "829", "849"]
41747       ],
41748       [
41749         "Ecuador",
41750         "ec",
41751         "593"
41752       ],
41753       [
41754         "Egypt (‫مصر‬‎)",
41755         "eg",
41756         "20"
41757       ],
41758       [
41759         "El Salvador",
41760         "sv",
41761         "503"
41762       ],
41763       [
41764         "Equatorial Guinea (Guinea Ecuatorial)",
41765         "gq",
41766         "240"
41767       ],
41768       [
41769         "Eritrea",
41770         "er",
41771         "291"
41772       ],
41773       [
41774         "Estonia (Eesti)",
41775         "ee",
41776         "372"
41777       ],
41778       [
41779         "Ethiopia",
41780         "et",
41781         "251"
41782       ],
41783       [
41784         "Falkland Islands (Islas Malvinas)",
41785         "fk",
41786         "500"
41787       ],
41788       [
41789         "Faroe Islands (Føroyar)",
41790         "fo",
41791         "298"
41792       ],
41793       [
41794         "Fiji",
41795         "fj",
41796         "679"
41797       ],
41798       [
41799         "Finland (Suomi)",
41800         "fi",
41801         "358",
41802         0
41803       ],
41804       [
41805         "France",
41806         "fr",
41807         "33"
41808       ],
41809       [
41810         "French Guiana (Guyane française)",
41811         "gf",
41812         "594"
41813       ],
41814       [
41815         "French Polynesia (Polynésie française)",
41816         "pf",
41817         "689"
41818       ],
41819       [
41820         "Gabon",
41821         "ga",
41822         "241"
41823       ],
41824       [
41825         "Gambia",
41826         "gm",
41827         "220"
41828       ],
41829       [
41830         "Georgia (საქართველო)",
41831         "ge",
41832         "995"
41833       ],
41834       [
41835         "Germany (Deutschland)",
41836         "de",
41837         "49"
41838       ],
41839       [
41840         "Ghana (Gaana)",
41841         "gh",
41842         "233"
41843       ],
41844       [
41845         "Gibraltar",
41846         "gi",
41847         "350"
41848       ],
41849       [
41850         "Greece (Ελλάδα)",
41851         "gr",
41852         "30"
41853       ],
41854       [
41855         "Greenland (Kalaallit Nunaat)",
41856         "gl",
41857         "299"
41858       ],
41859       [
41860         "Grenada",
41861         "gd",
41862         "1473"
41863       ],
41864       [
41865         "Guadeloupe",
41866         "gp",
41867         "590",
41868         0
41869       ],
41870       [
41871         "Guam",
41872         "gu",
41873         "1671"
41874       ],
41875       [
41876         "Guatemala",
41877         "gt",
41878         "502"
41879       ],
41880       [
41881         "Guernsey",
41882         "gg",
41883         "44",
41884         1
41885       ],
41886       [
41887         "Guinea (Guinée)",
41888         "gn",
41889         "224"
41890       ],
41891       [
41892         "Guinea-Bissau (Guiné Bissau)",
41893         "gw",
41894         "245"
41895       ],
41896       [
41897         "Guyana",
41898         "gy",
41899         "592"
41900       ],
41901       [
41902         "Haiti",
41903         "ht",
41904         "509"
41905       ],
41906       [
41907         "Honduras",
41908         "hn",
41909         "504"
41910       ],
41911       [
41912         "Hong Kong (香港)",
41913         "hk",
41914         "852"
41915       ],
41916       [
41917         "Hungary (Magyarország)",
41918         "hu",
41919         "36"
41920       ],
41921       [
41922         "Iceland (Ísland)",
41923         "is",
41924         "354"
41925       ],
41926       [
41927         "India (भारत)",
41928         "in",
41929         "91"
41930       ],
41931       [
41932         "Indonesia",
41933         "id",
41934         "62"
41935       ],
41936       [
41937         "Iran (‫ایران‬‎)",
41938         "ir",
41939         "98"
41940       ],
41941       [
41942         "Iraq (‫العراق‬‎)",
41943         "iq",
41944         "964"
41945       ],
41946       [
41947         "Ireland",
41948         "ie",
41949         "353"
41950       ],
41951       [
41952         "Isle of Man",
41953         "im",
41954         "44",
41955         2
41956       ],
41957       [
41958         "Israel (‫ישראל‬‎)",
41959         "il",
41960         "972"
41961       ],
41962       [
41963         "Italy (Italia)",
41964         "it",
41965         "39",
41966         0
41967       ],
41968       [
41969         "Jamaica",
41970         "jm",
41971         "1876"
41972       ],
41973       [
41974         "Japan (日本)",
41975         "jp",
41976         "81"
41977       ],
41978       [
41979         "Jersey",
41980         "je",
41981         "44",
41982         3
41983       ],
41984       [
41985         "Jordan (‫الأردن‬‎)",
41986         "jo",
41987         "962"
41988       ],
41989       [
41990         "Kazakhstan (Казахстан)",
41991         "kz",
41992         "7",
41993         1
41994       ],
41995       [
41996         "Kenya",
41997         "ke",
41998         "254"
41999       ],
42000       [
42001         "Kiribati",
42002         "ki",
42003         "686"
42004       ],
42005       [
42006         "Kosovo",
42007         "xk",
42008         "383"
42009       ],
42010       [
42011         "Kuwait (‫الكويت‬‎)",
42012         "kw",
42013         "965"
42014       ],
42015       [
42016         "Kyrgyzstan (Кыргызстан)",
42017         "kg",
42018         "996"
42019       ],
42020       [
42021         "Laos (ລາວ)",
42022         "la",
42023         "856"
42024       ],
42025       [
42026         "Latvia (Latvija)",
42027         "lv",
42028         "371"
42029       ],
42030       [
42031         "Lebanon (‫لبنان‬‎)",
42032         "lb",
42033         "961"
42034       ],
42035       [
42036         "Lesotho",
42037         "ls",
42038         "266"
42039       ],
42040       [
42041         "Liberia",
42042         "lr",
42043         "231"
42044       ],
42045       [
42046         "Libya (‫ليبيا‬‎)",
42047         "ly",
42048         "218"
42049       ],
42050       [
42051         "Liechtenstein",
42052         "li",
42053         "423"
42054       ],
42055       [
42056         "Lithuania (Lietuva)",
42057         "lt",
42058         "370"
42059       ],
42060       [
42061         "Luxembourg",
42062         "lu",
42063         "352"
42064       ],
42065       [
42066         "Macau (澳門)",
42067         "mo",
42068         "853"
42069       ],
42070       [
42071         "Macedonia (FYROM) (Македонија)",
42072         "mk",
42073         "389"
42074       ],
42075       [
42076         "Madagascar (Madagasikara)",
42077         "mg",
42078         "261"
42079       ],
42080       [
42081         "Malawi",
42082         "mw",
42083         "265"
42084       ],
42085       [
42086         "Malaysia",
42087         "my",
42088         "60"
42089       ],
42090       [
42091         "Maldives",
42092         "mv",
42093         "960"
42094       ],
42095       [
42096         "Mali",
42097         "ml",
42098         "223"
42099       ],
42100       [
42101         "Malta",
42102         "mt",
42103         "356"
42104       ],
42105       [
42106         "Marshall Islands",
42107         "mh",
42108         "692"
42109       ],
42110       [
42111         "Martinique",
42112         "mq",
42113         "596"
42114       ],
42115       [
42116         "Mauritania (‫موريتانيا‬‎)",
42117         "mr",
42118         "222"
42119       ],
42120       [
42121         "Mauritius (Moris)",
42122         "mu",
42123         "230"
42124       ],
42125       [
42126         "Mayotte",
42127         "yt",
42128         "262",
42129         1
42130       ],
42131       [
42132         "Mexico (México)",
42133         "mx",
42134         "52"
42135       ],
42136       [
42137         "Micronesia",
42138         "fm",
42139         "691"
42140       ],
42141       [
42142         "Moldova (Republica Moldova)",
42143         "md",
42144         "373"
42145       ],
42146       [
42147         "Monaco",
42148         "mc",
42149         "377"
42150       ],
42151       [
42152         "Mongolia (Монгол)",
42153         "mn",
42154         "976"
42155       ],
42156       [
42157         "Montenegro (Crna Gora)",
42158         "me",
42159         "382"
42160       ],
42161       [
42162         "Montserrat",
42163         "ms",
42164         "1664"
42165       ],
42166       [
42167         "Morocco (‫المغرب‬‎)",
42168         "ma",
42169         "212",
42170         0
42171       ],
42172       [
42173         "Mozambique (Moçambique)",
42174         "mz",
42175         "258"
42176       ],
42177       [
42178         "Myanmar (Burma) (မြန်မာ)",
42179         "mm",
42180         "95"
42181       ],
42182       [
42183         "Namibia (Namibië)",
42184         "na",
42185         "264"
42186       ],
42187       [
42188         "Nauru",
42189         "nr",
42190         "674"
42191       ],
42192       [
42193         "Nepal (नेपाल)",
42194         "np",
42195         "977"
42196       ],
42197       [
42198         "Netherlands (Nederland)",
42199         "nl",
42200         "31"
42201       ],
42202       [
42203         "New Caledonia (Nouvelle-Calédonie)",
42204         "nc",
42205         "687"
42206       ],
42207       [
42208         "New Zealand",
42209         "nz",
42210         "64"
42211       ],
42212       [
42213         "Nicaragua",
42214         "ni",
42215         "505"
42216       ],
42217       [
42218         "Niger (Nijar)",
42219         "ne",
42220         "227"
42221       ],
42222       [
42223         "Nigeria",
42224         "ng",
42225         "234"
42226       ],
42227       [
42228         "Niue",
42229         "nu",
42230         "683"
42231       ],
42232       [
42233         "Norfolk Island",
42234         "nf",
42235         "672"
42236       ],
42237       [
42238         "North Korea (조선 민주주의 인민 공화국)",
42239         "kp",
42240         "850"
42241       ],
42242       [
42243         "Northern Mariana Islands",
42244         "mp",
42245         "1670"
42246       ],
42247       [
42248         "Norway (Norge)",
42249         "no",
42250         "47",
42251         0
42252       ],
42253       [
42254         "Oman (‫عُمان‬‎)",
42255         "om",
42256         "968"
42257       ],
42258       [
42259         "Pakistan (‫پاکستان‬‎)",
42260         "pk",
42261         "92"
42262       ],
42263       [
42264         "Palau",
42265         "pw",
42266         "680"
42267       ],
42268       [
42269         "Palestine (‫فلسطين‬‎)",
42270         "ps",
42271         "970"
42272       ],
42273       [
42274         "Panama (Panamá)",
42275         "pa",
42276         "507"
42277       ],
42278       [
42279         "Papua New Guinea",
42280         "pg",
42281         "675"
42282       ],
42283       [
42284         "Paraguay",
42285         "py",
42286         "595"
42287       ],
42288       [
42289         "Peru (Perú)",
42290         "pe",
42291         "51"
42292       ],
42293       [
42294         "Philippines",
42295         "ph",
42296         "63"
42297       ],
42298       [
42299         "Poland (Polska)",
42300         "pl",
42301         "48"
42302       ],
42303       [
42304         "Portugal",
42305         "pt",
42306         "351"
42307       ],
42308       [
42309         "Puerto Rico",
42310         "pr",
42311         "1",
42312         3,
42313         ["787", "939"]
42314       ],
42315       [
42316         "Qatar (‫قطر‬‎)",
42317         "qa",
42318         "974"
42319       ],
42320       [
42321         "Réunion (La Réunion)",
42322         "re",
42323         "262",
42324         0
42325       ],
42326       [
42327         "Romania (România)",
42328         "ro",
42329         "40"
42330       ],
42331       [
42332         "Russia (Россия)",
42333         "ru",
42334         "7",
42335         0
42336       ],
42337       [
42338         "Rwanda",
42339         "rw",
42340         "250"
42341       ],
42342       [
42343         "Saint Barthélemy",
42344         "bl",
42345         "590",
42346         1
42347       ],
42348       [
42349         "Saint Helena",
42350         "sh",
42351         "290"
42352       ],
42353       [
42354         "Saint Kitts and Nevis",
42355         "kn",
42356         "1869"
42357       ],
42358       [
42359         "Saint Lucia",
42360         "lc",
42361         "1758"
42362       ],
42363       [
42364         "Saint Martin (Saint-Martin (partie française))",
42365         "mf",
42366         "590",
42367         2
42368       ],
42369       [
42370         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42371         "pm",
42372         "508"
42373       ],
42374       [
42375         "Saint Vincent and the Grenadines",
42376         "vc",
42377         "1784"
42378       ],
42379       [
42380         "Samoa",
42381         "ws",
42382         "685"
42383       ],
42384       [
42385         "San Marino",
42386         "sm",
42387         "378"
42388       ],
42389       [
42390         "São Tomé and Príncipe (São Tomé e Príncipe)",
42391         "st",
42392         "239"
42393       ],
42394       [
42395         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42396         "sa",
42397         "966"
42398       ],
42399       [
42400         "Senegal (Sénégal)",
42401         "sn",
42402         "221"
42403       ],
42404       [
42405         "Serbia (Србија)",
42406         "rs",
42407         "381"
42408       ],
42409       [
42410         "Seychelles",
42411         "sc",
42412         "248"
42413       ],
42414       [
42415         "Sierra Leone",
42416         "sl",
42417         "232"
42418       ],
42419       [
42420         "Singapore",
42421         "sg",
42422         "65"
42423       ],
42424       [
42425         "Sint Maarten",
42426         "sx",
42427         "1721"
42428       ],
42429       [
42430         "Slovakia (Slovensko)",
42431         "sk",
42432         "421"
42433       ],
42434       [
42435         "Slovenia (Slovenija)",
42436         "si",
42437         "386"
42438       ],
42439       [
42440         "Solomon Islands",
42441         "sb",
42442         "677"
42443       ],
42444       [
42445         "Somalia (Soomaaliya)",
42446         "so",
42447         "252"
42448       ],
42449       [
42450         "South Africa",
42451         "za",
42452         "27"
42453       ],
42454       [
42455         "South Korea (대한민국)",
42456         "kr",
42457         "82"
42458       ],
42459       [
42460         "South Sudan (‫جنوب السودان‬‎)",
42461         "ss",
42462         "211"
42463       ],
42464       [
42465         "Spain (España)",
42466         "es",
42467         "34"
42468       ],
42469       [
42470         "Sri Lanka (ශ්‍රී ලංකාව)",
42471         "lk",
42472         "94"
42473       ],
42474       [
42475         "Sudan (‫السودان‬‎)",
42476         "sd",
42477         "249"
42478       ],
42479       [
42480         "Suriname",
42481         "sr",
42482         "597"
42483       ],
42484       [
42485         "Svalbard and Jan Mayen",
42486         "sj",
42487         "47",
42488         1
42489       ],
42490       [
42491         "Swaziland",
42492         "sz",
42493         "268"
42494       ],
42495       [
42496         "Sweden (Sverige)",
42497         "se",
42498         "46"
42499       ],
42500       [
42501         "Switzerland (Schweiz)",
42502         "ch",
42503         "41"
42504       ],
42505       [
42506         "Syria (‫سوريا‬‎)",
42507         "sy",
42508         "963"
42509       ],
42510       [
42511         "Taiwan (台灣)",
42512         "tw",
42513         "886"
42514       ],
42515       [
42516         "Tajikistan",
42517         "tj",
42518         "992"
42519       ],
42520       [
42521         "Tanzania",
42522         "tz",
42523         "255"
42524       ],
42525       [
42526         "Thailand (ไทย)",
42527         "th",
42528         "66"
42529       ],
42530       [
42531         "Timor-Leste",
42532         "tl",
42533         "670"
42534       ],
42535       [
42536         "Togo",
42537         "tg",
42538         "228"
42539       ],
42540       [
42541         "Tokelau",
42542         "tk",
42543         "690"
42544       ],
42545       [
42546         "Tonga",
42547         "to",
42548         "676"
42549       ],
42550       [
42551         "Trinidad and Tobago",
42552         "tt",
42553         "1868"
42554       ],
42555       [
42556         "Tunisia (‫تونس‬‎)",
42557         "tn",
42558         "216"
42559       ],
42560       [
42561         "Turkey (Türkiye)",
42562         "tr",
42563         "90"
42564       ],
42565       [
42566         "Turkmenistan",
42567         "tm",
42568         "993"
42569       ],
42570       [
42571         "Turks and Caicos Islands",
42572         "tc",
42573         "1649"
42574       ],
42575       [
42576         "Tuvalu",
42577         "tv",
42578         "688"
42579       ],
42580       [
42581         "U.S. Virgin Islands",
42582         "vi",
42583         "1340"
42584       ],
42585       [
42586         "Uganda",
42587         "ug",
42588         "256"
42589       ],
42590       [
42591         "Ukraine (Україна)",
42592         "ua",
42593         "380"
42594       ],
42595       [
42596         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42597         "ae",
42598         "971"
42599       ],
42600       [
42601         "United Kingdom",
42602         "gb",
42603         "44",
42604         0
42605       ],
42606       [
42607         "United States",
42608         "us",
42609         "1",
42610         0
42611       ],
42612       [
42613         "Uruguay",
42614         "uy",
42615         "598"
42616       ],
42617       [
42618         "Uzbekistan (Oʻzbekiston)",
42619         "uz",
42620         "998"
42621       ],
42622       [
42623         "Vanuatu",
42624         "vu",
42625         "678"
42626       ],
42627       [
42628         "Vatican City (Città del Vaticano)",
42629         "va",
42630         "39",
42631         1
42632       ],
42633       [
42634         "Venezuela",
42635         "ve",
42636         "58"
42637       ],
42638       [
42639         "Vietnam (Việt Nam)",
42640         "vn",
42641         "84"
42642       ],
42643       [
42644         "Wallis and Futuna (Wallis-et-Futuna)",
42645         "wf",
42646         "681"
42647       ],
42648       [
42649         "Western Sahara (‫الصحراء الغربية‬‎)",
42650         "eh",
42651         "212",
42652         1
42653       ],
42654       [
42655         "Yemen (‫اليمن‬‎)",
42656         "ye",
42657         "967"
42658       ],
42659       [
42660         "Zambia",
42661         "zm",
42662         "260"
42663       ],
42664       [
42665         "Zimbabwe",
42666         "zw",
42667         "263"
42668       ],
42669       [
42670         "Åland Islands",
42671         "ax",
42672         "358",
42673         1
42674       ]
42675   ];
42676   
42677   return d;
42678 }/**
42679 *    This script refer to:
42680 *    Title: International Telephone Input
42681 *    Author: Jack O'Connor
42682 *    Code version:  v12.1.12
42683 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42684 **/
42685
42686 /**
42687  * @class Roo.bootstrap.PhoneInput
42688  * @extends Roo.bootstrap.TriggerField
42689  * An input with International dial-code selection
42690  
42691  * @cfg {String} defaultDialCode default '+852'
42692  * @cfg {Array} preferedCountries default []
42693   
42694  * @constructor
42695  * Create a new PhoneInput.
42696  * @param {Object} config Configuration options
42697  */
42698
42699 Roo.bootstrap.PhoneInput = function(config) {
42700     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42701 };
42702
42703 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42704         
42705         listWidth: undefined,
42706         
42707         selectedClass: 'active',
42708         
42709         invalidClass : "has-warning",
42710         
42711         validClass: 'has-success',
42712         
42713         allowed: '0123456789',
42714         
42715         max_length: 15,
42716         
42717         /**
42718          * @cfg {String} defaultDialCode The default dial code when initializing the input
42719          */
42720         defaultDialCode: '+852',
42721         
42722         /**
42723          * @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
42724          */
42725         preferedCountries: false,
42726         
42727         getAutoCreate : function()
42728         {
42729             var data = Roo.bootstrap.PhoneInputData();
42730             var align = this.labelAlign || this.parentLabelAlign();
42731             var id = Roo.id();
42732             
42733             this.allCountries = [];
42734             this.dialCodeMapping = [];
42735             
42736             for (var i = 0; i < data.length; i++) {
42737               var c = data[i];
42738               this.allCountries[i] = {
42739                 name: c[0],
42740                 iso2: c[1],
42741                 dialCode: c[2],
42742                 priority: c[3] || 0,
42743                 areaCodes: c[4] || null
42744               };
42745               this.dialCodeMapping[c[2]] = {
42746                   name: c[0],
42747                   iso2: c[1],
42748                   priority: c[3] || 0,
42749                   areaCodes: c[4] || null
42750               };
42751             }
42752             
42753             var cfg = {
42754                 cls: 'form-group',
42755                 cn: []
42756             };
42757             
42758             var input =  {
42759                 tag: 'input',
42760                 id : id,
42761                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42762                 maxlength: this.max_length,
42763                 cls : 'form-control tel-input',
42764                 autocomplete: 'new-password'
42765             };
42766             
42767             var hiddenInput = {
42768                 tag: 'input',
42769                 type: 'hidden',
42770                 cls: 'hidden-tel-input'
42771             };
42772             
42773             if (this.name) {
42774                 hiddenInput.name = this.name;
42775             }
42776             
42777             if (this.disabled) {
42778                 input.disabled = true;
42779             }
42780             
42781             var flag_container = {
42782                 tag: 'div',
42783                 cls: 'flag-box',
42784                 cn: [
42785                     {
42786                         tag: 'div',
42787                         cls: 'flag'
42788                     },
42789                     {
42790                         tag: 'div',
42791                         cls: 'caret'
42792                     }
42793                 ]
42794             };
42795             
42796             var box = {
42797                 tag: 'div',
42798                 cls: this.hasFeedback ? 'has-feedback' : '',
42799                 cn: [
42800                     hiddenInput,
42801                     input,
42802                     {
42803                         tag: 'input',
42804                         cls: 'dial-code-holder',
42805                         disabled: true
42806                     }
42807                 ]
42808             };
42809             
42810             var container = {
42811                 cls: 'roo-select2-container input-group',
42812                 cn: [
42813                     flag_container,
42814                     box
42815                 ]
42816             };
42817             
42818             if (this.fieldLabel.length) {
42819                 var indicator = {
42820                     tag: 'i',
42821                     tooltip: 'This field is required'
42822                 };
42823                 
42824                 var label = {
42825                     tag: 'label',
42826                     'for':  id,
42827                     cls: 'control-label',
42828                     cn: []
42829                 };
42830                 
42831                 var label_text = {
42832                     tag: 'span',
42833                     html: this.fieldLabel
42834                 };
42835                 
42836                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42837                 label.cn = [
42838                     indicator,
42839                     label_text
42840                 ];
42841                 
42842                 if(this.indicatorpos == 'right') {
42843                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42844                     label.cn = [
42845                         label_text,
42846                         indicator
42847                     ];
42848                 }
42849                 
42850                 if(align == 'left') {
42851                     container = {
42852                         tag: 'div',
42853                         cn: [
42854                             container
42855                         ]
42856                     };
42857                     
42858                     if(this.labelWidth > 12){
42859                         label.style = "width: " + this.labelWidth + 'px';
42860                     }
42861                     if(this.labelWidth < 13 && this.labelmd == 0){
42862                         this.labelmd = this.labelWidth;
42863                     }
42864                     if(this.labellg > 0){
42865                         label.cls += ' col-lg-' + this.labellg;
42866                         input.cls += ' col-lg-' + (12 - this.labellg);
42867                     }
42868                     if(this.labelmd > 0){
42869                         label.cls += ' col-md-' + this.labelmd;
42870                         container.cls += ' col-md-' + (12 - this.labelmd);
42871                     }
42872                     if(this.labelsm > 0){
42873                         label.cls += ' col-sm-' + this.labelsm;
42874                         container.cls += ' col-sm-' + (12 - this.labelsm);
42875                     }
42876                     if(this.labelxs > 0){
42877                         label.cls += ' col-xs-' + this.labelxs;
42878                         container.cls += ' col-xs-' + (12 - this.labelxs);
42879                     }
42880                 }
42881             }
42882             
42883             cfg.cn = [
42884                 label,
42885                 container
42886             ];
42887             
42888             var settings = this;
42889             
42890             ['xs','sm','md','lg'].map(function(size){
42891                 if (settings[size]) {
42892                     cfg.cls += ' col-' + size + '-' + settings[size];
42893                 }
42894             });
42895             
42896             this.store = new Roo.data.Store({
42897                 proxy : new Roo.data.MemoryProxy({}),
42898                 reader : new Roo.data.JsonReader({
42899                     fields : [
42900                         {
42901                             'name' : 'name',
42902                             'type' : 'string'
42903                         },
42904                         {
42905                             'name' : 'iso2',
42906                             'type' : 'string'
42907                         },
42908                         {
42909                             'name' : 'dialCode',
42910                             'type' : 'string'
42911                         },
42912                         {
42913                             'name' : 'priority',
42914                             'type' : 'string'
42915                         },
42916                         {
42917                             'name' : 'areaCodes',
42918                             'type' : 'string'
42919                         }
42920                     ]
42921                 })
42922             });
42923             
42924             if(!this.preferedCountries) {
42925                 this.preferedCountries = [
42926                     'hk',
42927                     'gb',
42928                     'us'
42929                 ];
42930             }
42931             
42932             var p = this.preferedCountries.reverse();
42933             
42934             if(p) {
42935                 for (var i = 0; i < p.length; i++) {
42936                     for (var j = 0; j < this.allCountries.length; j++) {
42937                         if(this.allCountries[j].iso2 == p[i]) {
42938                             var t = this.allCountries[j];
42939                             this.allCountries.splice(j,1);
42940                             this.allCountries.unshift(t);
42941                         }
42942                     } 
42943                 }
42944             }
42945             
42946             this.store.proxy.data = {
42947                 success: true,
42948                 data: this.allCountries
42949             };
42950             
42951             return cfg;
42952         },
42953         
42954         initEvents : function()
42955         {
42956             this.createList();
42957             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42958             
42959             this.indicator = this.indicatorEl();
42960             this.flag = this.flagEl();
42961             this.dialCodeHolder = this.dialCodeHolderEl();
42962             
42963             this.trigger = this.el.select('div.flag-box',true).first();
42964             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42965             
42966             var _this = this;
42967             
42968             (function(){
42969                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42970                 _this.list.setWidth(lw);
42971             }).defer(100);
42972             
42973             this.list.on('mouseover', this.onViewOver, this);
42974             this.list.on('mousemove', this.onViewMove, this);
42975             this.inputEl().on("keyup", this.onKeyUp, this);
42976             this.inputEl().on("keypress", this.onKeyPress, this);
42977             
42978             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42979
42980             this.view = new Roo.View(this.list, this.tpl, {
42981                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42982             });
42983             
42984             this.view.on('click', this.onViewClick, this);
42985             this.setValue(this.defaultDialCode);
42986         },
42987         
42988         onTriggerClick : function(e)
42989         {
42990             Roo.log('trigger click');
42991             if(this.disabled){
42992                 return;
42993             }
42994             
42995             if(this.isExpanded()){
42996                 this.collapse();
42997                 this.hasFocus = false;
42998             }else {
42999                 this.store.load({});
43000                 this.hasFocus = true;
43001                 this.expand();
43002             }
43003         },
43004         
43005         isExpanded : function()
43006         {
43007             return this.list.isVisible();
43008         },
43009         
43010         collapse : function()
43011         {
43012             if(!this.isExpanded()){
43013                 return;
43014             }
43015             this.list.hide();
43016             Roo.get(document).un('mousedown', this.collapseIf, this);
43017             Roo.get(document).un('mousewheel', this.collapseIf, this);
43018             this.fireEvent('collapse', this);
43019             this.validate();
43020         },
43021         
43022         expand : function()
43023         {
43024             Roo.log('expand');
43025
43026             if(this.isExpanded() || !this.hasFocus){
43027                 return;
43028             }
43029             
43030             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43031             this.list.setWidth(lw);
43032             
43033             this.list.show();
43034             this.restrictHeight();
43035             
43036             Roo.get(document).on('mousedown', this.collapseIf, this);
43037             Roo.get(document).on('mousewheel', this.collapseIf, this);
43038             
43039             this.fireEvent('expand', this);
43040         },
43041         
43042         restrictHeight : function()
43043         {
43044             this.list.alignTo(this.inputEl(), this.listAlign);
43045             this.list.alignTo(this.inputEl(), this.listAlign);
43046         },
43047         
43048         onViewOver : function(e, t)
43049         {
43050             if(this.inKeyMode){
43051                 return;
43052             }
43053             var item = this.view.findItemFromChild(t);
43054             
43055             if(item){
43056                 var index = this.view.indexOf(item);
43057                 this.select(index, false);
43058             }
43059         },
43060
43061         // private
43062         onViewClick : function(view, doFocus, el, e)
43063         {
43064             var index = this.view.getSelectedIndexes()[0];
43065             
43066             var r = this.store.getAt(index);
43067             
43068             if(r){
43069                 this.onSelect(r, index);
43070             }
43071             if(doFocus !== false && !this.blockFocus){
43072                 this.inputEl().focus();
43073             }
43074         },
43075         
43076         onViewMove : function(e, t)
43077         {
43078             this.inKeyMode = false;
43079         },
43080         
43081         select : function(index, scrollIntoView)
43082         {
43083             this.selectedIndex = index;
43084             this.view.select(index);
43085             if(scrollIntoView !== false){
43086                 var el = this.view.getNode(index);
43087                 if(el){
43088                     this.list.scrollChildIntoView(el, false);
43089                 }
43090             }
43091         },
43092         
43093         createList : function()
43094         {
43095             this.list = Roo.get(document.body).createChild({
43096                 tag: 'ul',
43097                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43098                 style: 'display:none'
43099             });
43100             
43101             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43102         },
43103         
43104         collapseIf : function(e)
43105         {
43106             var in_combo  = e.within(this.el);
43107             var in_list =  e.within(this.list);
43108             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43109             
43110             if (in_combo || in_list || is_list) {
43111                 return;
43112             }
43113             this.collapse();
43114         },
43115         
43116         onSelect : function(record, index)
43117         {
43118             if(this.fireEvent('beforeselect', this, record, index) !== false){
43119                 
43120                 this.setFlagClass(record.data.iso2);
43121                 this.setDialCode(record.data.dialCode);
43122                 this.hasFocus = false;
43123                 this.collapse();
43124                 this.fireEvent('select', this, record, index);
43125             }
43126         },
43127         
43128         flagEl : function()
43129         {
43130             var flag = this.el.select('div.flag',true).first();
43131             if(!flag){
43132                 return false;
43133             }
43134             return flag;
43135         },
43136         
43137         dialCodeHolderEl : function()
43138         {
43139             var d = this.el.select('input.dial-code-holder',true).first();
43140             if(!d){
43141                 return false;
43142             }
43143             return d;
43144         },
43145         
43146         setDialCode : function(v)
43147         {
43148             this.dialCodeHolder.dom.value = '+'+v;
43149         },
43150         
43151         setFlagClass : function(n)
43152         {
43153             this.flag.dom.className = 'flag '+n;
43154         },
43155         
43156         getValue : function()
43157         {
43158             var v = this.inputEl().getValue();
43159             if(this.dialCodeHolder) {
43160                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43161             }
43162             return v;
43163         },
43164         
43165         setValue : function(v)
43166         {
43167             var d = this.getDialCode(v);
43168             
43169             //invalid dial code
43170             if(v.length == 0 || !d || d.length == 0) {
43171                 if(this.rendered){
43172                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43173                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43174                 }
43175                 return;
43176             }
43177             
43178             //valid dial code
43179             this.setFlagClass(this.dialCodeMapping[d].iso2);
43180             this.setDialCode(d);
43181             this.inputEl().dom.value = v.replace('+'+d,'');
43182             this.hiddenEl().dom.value = this.getValue();
43183             
43184             this.validate();
43185         },
43186         
43187         getDialCode : function(v)
43188         {
43189             v = v ||  '';
43190             
43191             if (v.length == 0) {
43192                 return this.dialCodeHolder.dom.value;
43193             }
43194             
43195             var dialCode = "";
43196             if (v.charAt(0) != "+") {
43197                 return false;
43198             }
43199             var numericChars = "";
43200             for (var i = 1; i < v.length; i++) {
43201               var c = v.charAt(i);
43202               if (!isNaN(c)) {
43203                 numericChars += c;
43204                 if (this.dialCodeMapping[numericChars]) {
43205                   dialCode = v.substr(1, i);
43206                 }
43207                 if (numericChars.length == 4) {
43208                   break;
43209                 }
43210               }
43211             }
43212             return dialCode;
43213         },
43214         
43215         reset : function()
43216         {
43217             this.setValue(this.defaultDialCode);
43218             this.validate();
43219         },
43220         
43221         hiddenEl : function()
43222         {
43223             return this.el.select('input.hidden-tel-input',true).first();
43224         },
43225         
43226         // after setting val
43227         onKeyUp : function(e){
43228             this.setValue(this.getValue());
43229         },
43230         
43231         onKeyPress : function(e){
43232             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43233                 e.stopEvent();
43234             }
43235         }
43236         
43237 });
43238 /**
43239  * @class Roo.bootstrap.MoneyField
43240  * @extends Roo.bootstrap.ComboBox
43241  * Bootstrap MoneyField class
43242  * 
43243  * @constructor
43244  * Create a new MoneyField.
43245  * @param {Object} config Configuration options
43246  */
43247
43248 Roo.bootstrap.MoneyField = function(config) {
43249     
43250     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43251     
43252 };
43253
43254 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43255     
43256     /**
43257      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43258      */
43259     allowDecimals : true,
43260     /**
43261      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43262      */
43263     decimalSeparator : ".",
43264     /**
43265      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43266      */
43267     decimalPrecision : 0,
43268     /**
43269      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43270      */
43271     allowNegative : true,
43272     /**
43273      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43274      */
43275     allowZero: true,
43276     /**
43277      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43278      */
43279     minValue : Number.NEGATIVE_INFINITY,
43280     /**
43281      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43282      */
43283     maxValue : Number.MAX_VALUE,
43284     /**
43285      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43286      */
43287     minText : "The minimum value for this field is {0}",
43288     /**
43289      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43290      */
43291     maxText : "The maximum value for this field is {0}",
43292     /**
43293      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43294      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43295      */
43296     nanText : "{0} is not a valid number",
43297     /**
43298      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43299      */
43300     castInt : true,
43301     /**
43302      * @cfg {String} defaults currency of the MoneyField
43303      * value should be in lkey
43304      */
43305     defaultCurrency : false,
43306     /**
43307      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43308      */
43309     thousandsDelimiter : false,
43310     /**
43311      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43312      */
43313     max_length: false,
43314     
43315     inputlg : 9,
43316     inputmd : 9,
43317     inputsm : 9,
43318     inputxs : 6,
43319     
43320     store : false,
43321     
43322     getAutoCreate : function()
43323     {
43324         var align = this.labelAlign || this.parentLabelAlign();
43325         
43326         var id = Roo.id();
43327
43328         var cfg = {
43329             cls: 'form-group',
43330             cn: []
43331         };
43332
43333         var input =  {
43334             tag: 'input',
43335             id : id,
43336             cls : 'form-control roo-money-amount-input',
43337             autocomplete: 'new-password'
43338         };
43339         
43340         var hiddenInput = {
43341             tag: 'input',
43342             type: 'hidden',
43343             id: Roo.id(),
43344             cls: 'hidden-number-input'
43345         };
43346         
43347         if(this.max_length) {
43348             input.maxlength = this.max_length; 
43349         }
43350         
43351         if (this.name) {
43352             hiddenInput.name = this.name;
43353         }
43354
43355         if (this.disabled) {
43356             input.disabled = true;
43357         }
43358
43359         var clg = 12 - this.inputlg;
43360         var cmd = 12 - this.inputmd;
43361         var csm = 12 - this.inputsm;
43362         var cxs = 12 - this.inputxs;
43363         
43364         var container = {
43365             tag : 'div',
43366             cls : 'row roo-money-field',
43367             cn : [
43368                 {
43369                     tag : 'div',
43370                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43371                     cn : [
43372                         {
43373                             tag : 'div',
43374                             cls: 'roo-select2-container input-group',
43375                             cn: [
43376                                 {
43377                                     tag : 'input',
43378                                     cls : 'form-control roo-money-currency-input',
43379                                     autocomplete: 'new-password',
43380                                     readOnly : 1,
43381                                     name : this.currencyName
43382                                 },
43383                                 {
43384                                     tag :'span',
43385                                     cls : 'input-group-addon',
43386                                     cn : [
43387                                         {
43388                                             tag: 'span',
43389                                             cls: 'caret'
43390                                         }
43391                                     ]
43392                                 }
43393                             ]
43394                         }
43395                     ]
43396                 },
43397                 {
43398                     tag : 'div',
43399                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43400                     cn : [
43401                         {
43402                             tag: 'div',
43403                             cls: this.hasFeedback ? 'has-feedback' : '',
43404                             cn: [
43405                                 input
43406                             ]
43407                         }
43408                     ]
43409                 }
43410             ]
43411             
43412         };
43413         
43414         if (this.fieldLabel.length) {
43415             var indicator = {
43416                 tag: 'i',
43417                 tooltip: 'This field is required'
43418             };
43419
43420             var label = {
43421                 tag: 'label',
43422                 'for':  id,
43423                 cls: 'control-label',
43424                 cn: []
43425             };
43426
43427             var label_text = {
43428                 tag: 'span',
43429                 html: this.fieldLabel
43430             };
43431
43432             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43433             label.cn = [
43434                 indicator,
43435                 label_text
43436             ];
43437
43438             if(this.indicatorpos == 'right') {
43439                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43440                 label.cn = [
43441                     label_text,
43442                     indicator
43443                 ];
43444             }
43445
43446             if(align == 'left') {
43447                 container = {
43448                     tag: 'div',
43449                     cn: [
43450                         container
43451                     ]
43452                 };
43453
43454                 if(this.labelWidth > 12){
43455                     label.style = "width: " + this.labelWidth + 'px';
43456                 }
43457                 if(this.labelWidth < 13 && this.labelmd == 0){
43458                     this.labelmd = this.labelWidth;
43459                 }
43460                 if(this.labellg > 0){
43461                     label.cls += ' col-lg-' + this.labellg;
43462                     input.cls += ' col-lg-' + (12 - this.labellg);
43463                 }
43464                 if(this.labelmd > 0){
43465                     label.cls += ' col-md-' + this.labelmd;
43466                     container.cls += ' col-md-' + (12 - this.labelmd);
43467                 }
43468                 if(this.labelsm > 0){
43469                     label.cls += ' col-sm-' + this.labelsm;
43470                     container.cls += ' col-sm-' + (12 - this.labelsm);
43471                 }
43472                 if(this.labelxs > 0){
43473                     label.cls += ' col-xs-' + this.labelxs;
43474                     container.cls += ' col-xs-' + (12 - this.labelxs);
43475                 }
43476             }
43477         }
43478
43479         cfg.cn = [
43480             label,
43481             container,
43482             hiddenInput
43483         ];
43484         
43485         var settings = this;
43486
43487         ['xs','sm','md','lg'].map(function(size){
43488             if (settings[size]) {
43489                 cfg.cls += ' col-' + size + '-' + settings[size];
43490             }
43491         });
43492         
43493         return cfg;
43494     },
43495     
43496     initEvents : function()
43497     {
43498         this.indicator = this.indicatorEl();
43499         
43500         this.initCurrencyEvent();
43501         
43502         this.initNumberEvent();
43503     },
43504     
43505     initCurrencyEvent : function()
43506     {
43507         if (!this.store) {
43508             throw "can not find store for combo";
43509         }
43510         
43511         this.store = Roo.factory(this.store, Roo.data);
43512         this.store.parent = this;
43513         
43514         this.createList();
43515         
43516         this.triggerEl = this.el.select('.input-group-addon', true).first();
43517         
43518         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43519         
43520         var _this = this;
43521         
43522         (function(){
43523             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43524             _this.list.setWidth(lw);
43525         }).defer(100);
43526         
43527         this.list.on('mouseover', this.onViewOver, this);
43528         this.list.on('mousemove', this.onViewMove, this);
43529         this.list.on('scroll', this.onViewScroll, this);
43530         
43531         if(!this.tpl){
43532             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43533         }
43534         
43535         this.view = new Roo.View(this.list, this.tpl, {
43536             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43537         });
43538         
43539         this.view.on('click', this.onViewClick, this);
43540         
43541         this.store.on('beforeload', this.onBeforeLoad, this);
43542         this.store.on('load', this.onLoad, this);
43543         this.store.on('loadexception', this.onLoadException, this);
43544         
43545         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43546             "up" : function(e){
43547                 this.inKeyMode = true;
43548                 this.selectPrev();
43549             },
43550
43551             "down" : function(e){
43552                 if(!this.isExpanded()){
43553                     this.onTriggerClick();
43554                 }else{
43555                     this.inKeyMode = true;
43556                     this.selectNext();
43557                 }
43558             },
43559
43560             "enter" : function(e){
43561                 this.collapse();
43562                 
43563                 if(this.fireEvent("specialkey", this, e)){
43564                     this.onViewClick(false);
43565                 }
43566                 
43567                 return true;
43568             },
43569
43570             "esc" : function(e){
43571                 this.collapse();
43572             },
43573
43574             "tab" : function(e){
43575                 this.collapse();
43576                 
43577                 if(this.fireEvent("specialkey", this, e)){
43578                     this.onViewClick(false);
43579                 }
43580                 
43581                 return true;
43582             },
43583
43584             scope : this,
43585
43586             doRelay : function(foo, bar, hname){
43587                 if(hname == 'down' || this.scope.isExpanded()){
43588                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43589                 }
43590                 return true;
43591             },
43592
43593             forceKeyDown: true
43594         });
43595         
43596         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43597         
43598     },
43599     
43600     initNumberEvent : function(e)
43601     {
43602         this.inputEl().on("keydown" , this.fireKey,  this);
43603         this.inputEl().on("focus", this.onFocus,  this);
43604         this.inputEl().on("blur", this.onBlur,  this);
43605         
43606         this.inputEl().relayEvent('keyup', this);
43607         
43608         if(this.indicator){
43609             this.indicator.addClass('invisible');
43610         }
43611  
43612         this.originalValue = this.getValue();
43613         
43614         if(this.validationEvent == 'keyup'){
43615             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43616             this.inputEl().on('keyup', this.filterValidation, this);
43617         }
43618         else if(this.validationEvent !== false){
43619             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43620         }
43621         
43622         if(this.selectOnFocus){
43623             this.on("focus", this.preFocus, this);
43624             
43625         }
43626         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43627             this.inputEl().on("keypress", this.filterKeys, this);
43628         } else {
43629             this.inputEl().relayEvent('keypress', this);
43630         }
43631         
43632         var allowed = "0123456789";
43633         
43634         if(this.allowDecimals){
43635             allowed += this.decimalSeparator;
43636         }
43637         
43638         if(this.allowNegative){
43639             allowed += "-";
43640         }
43641         
43642         if(this.thousandsDelimiter) {
43643             allowed += ",";
43644         }
43645         
43646         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43647         
43648         var keyPress = function(e){
43649             
43650             var k = e.getKey();
43651             
43652             var c = e.getCharCode();
43653             
43654             if(
43655                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43656                     allowed.indexOf(String.fromCharCode(c)) === -1
43657             ){
43658                 e.stopEvent();
43659                 return;
43660             }
43661             
43662             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43663                 return;
43664             }
43665             
43666             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43667                 e.stopEvent();
43668             }
43669         };
43670         
43671         this.inputEl().on("keypress", keyPress, this);
43672         
43673     },
43674     
43675     onTriggerClick : function(e)
43676     {   
43677         if(this.disabled){
43678             return;
43679         }
43680         
43681         this.page = 0;
43682         this.loadNext = false;
43683         
43684         if(this.isExpanded()){
43685             this.collapse();
43686             return;
43687         }
43688         
43689         this.hasFocus = true;
43690         
43691         if(this.triggerAction == 'all') {
43692             this.doQuery(this.allQuery, true);
43693             return;
43694         }
43695         
43696         this.doQuery(this.getRawValue());
43697     },
43698     
43699     getCurrency : function()
43700     {   
43701         var v = this.currencyEl().getValue();
43702         
43703         return v;
43704     },
43705     
43706     restrictHeight : function()
43707     {
43708         this.list.alignTo(this.currencyEl(), this.listAlign);
43709         this.list.alignTo(this.currencyEl(), this.listAlign);
43710     },
43711     
43712     onViewClick : function(view, doFocus, el, e)
43713     {
43714         var index = this.view.getSelectedIndexes()[0];
43715         
43716         var r = this.store.getAt(index);
43717         
43718         if(r){
43719             this.onSelect(r, index);
43720         }
43721     },
43722     
43723     onSelect : function(record, index){
43724         
43725         if(this.fireEvent('beforeselect', this, record, index) !== false){
43726         
43727             this.setFromCurrencyData(index > -1 ? record.data : false);
43728             
43729             this.collapse();
43730             
43731             this.fireEvent('select', this, record, index);
43732         }
43733     },
43734     
43735     setFromCurrencyData : function(o)
43736     {
43737         var currency = '';
43738         
43739         this.lastCurrency = o;
43740         
43741         if (this.currencyField) {
43742             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43743         } else {
43744             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43745         }
43746         
43747         this.lastSelectionText = currency;
43748         
43749         //setting default currency
43750         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43751             this.setCurrency(this.defaultCurrency);
43752             return;
43753         }
43754         
43755         this.setCurrency(currency);
43756     },
43757     
43758     setFromData : function(o)
43759     {
43760         var c = {};
43761         
43762         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43763         
43764         this.setFromCurrencyData(c);
43765         
43766         var value = '';
43767         
43768         if (this.name) {
43769             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43770         } else {
43771             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43772         }
43773         
43774         this.setValue(value);
43775         
43776     },
43777     
43778     setCurrency : function(v)
43779     {   
43780         this.currencyValue = v;
43781         
43782         if(this.rendered){
43783             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43784             this.validate();
43785         }
43786     },
43787     
43788     setValue : function(v)
43789     {
43790         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43791         
43792         this.value = v;
43793         
43794         if(this.rendered){
43795             
43796             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43797             
43798             this.inputEl().dom.value = (v == '') ? '' :
43799                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43800             
43801             if(!this.allowZero && v === '0') {
43802                 this.hiddenEl().dom.value = '';
43803                 this.inputEl().dom.value = '';
43804             }
43805             
43806             this.validate();
43807         }
43808     },
43809     
43810     getRawValue : function()
43811     {
43812         var v = this.inputEl().getValue();
43813         
43814         return v;
43815     },
43816     
43817     getValue : function()
43818     {
43819         return this.fixPrecision(this.parseValue(this.getRawValue()));
43820     },
43821     
43822     parseValue : function(value)
43823     {
43824         if(this.thousandsDelimiter) {
43825             value += "";
43826             r = new RegExp(",", "g");
43827             value = value.replace(r, "");
43828         }
43829         
43830         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43831         return isNaN(value) ? '' : value;
43832         
43833     },
43834     
43835     fixPrecision : function(value)
43836     {
43837         if(this.thousandsDelimiter) {
43838             value += "";
43839             r = new RegExp(",", "g");
43840             value = value.replace(r, "");
43841         }
43842         
43843         var nan = isNaN(value);
43844         
43845         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43846             return nan ? '' : value;
43847         }
43848         return parseFloat(value).toFixed(this.decimalPrecision);
43849     },
43850     
43851     decimalPrecisionFcn : function(v)
43852     {
43853         return Math.floor(v);
43854     },
43855     
43856     validateValue : function(value)
43857     {
43858         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43859             return false;
43860         }
43861         
43862         var num = this.parseValue(value);
43863         
43864         if(isNaN(num)){
43865             this.markInvalid(String.format(this.nanText, value));
43866             return false;
43867         }
43868         
43869         if(num < this.minValue){
43870             this.markInvalid(String.format(this.minText, this.minValue));
43871             return false;
43872         }
43873         
43874         if(num > this.maxValue){
43875             this.markInvalid(String.format(this.maxText, this.maxValue));
43876             return false;
43877         }
43878         
43879         return true;
43880     },
43881     
43882     validate : function()
43883     {
43884         if(this.disabled || this.allowBlank){
43885             this.markValid();
43886             return true;
43887         }
43888         
43889         var currency = this.getCurrency();
43890         
43891         if(this.validateValue(this.getRawValue()) && currency.length){
43892             this.markValid();
43893             return true;
43894         }
43895         
43896         this.markInvalid();
43897         return false;
43898     },
43899     
43900     getName: function()
43901     {
43902         return this.name;
43903     },
43904     
43905     beforeBlur : function()
43906     {
43907         if(!this.castInt){
43908             return;
43909         }
43910         
43911         var v = this.parseValue(this.getRawValue());
43912         
43913         if(v || v == 0){
43914             this.setValue(v);
43915         }
43916     },
43917     
43918     onBlur : function()
43919     {
43920         this.beforeBlur();
43921         
43922         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43923             //this.el.removeClass(this.focusClass);
43924         }
43925         
43926         this.hasFocus = false;
43927         
43928         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43929             this.validate();
43930         }
43931         
43932         var v = this.getValue();
43933         
43934         if(String(v) !== String(this.startValue)){
43935             this.fireEvent('change', this, v, this.startValue);
43936         }
43937         
43938         this.fireEvent("blur", this);
43939     },
43940     
43941     inputEl : function()
43942     {
43943         return this.el.select('.roo-money-amount-input', true).first();
43944     },
43945     
43946     currencyEl : function()
43947     {
43948         return this.el.select('.roo-money-currency-input', true).first();
43949     },
43950     
43951     hiddenEl : function()
43952     {
43953         return this.el.select('input.hidden-number-input',true).first();
43954     }
43955     
43956 });/**
43957  * @class Roo.bootstrap.BezierSignature
43958  * @extends Roo.bootstrap.Component
43959  * Bootstrap BezierSignature class
43960  * This script refer to:
43961  *    Title: Signature Pad
43962  *    Author: szimek
43963  *    Availability: https://github.com/szimek/signature_pad
43964  *
43965  * @constructor
43966  * Create a new BezierSignature
43967  * @param {Object} config The config object
43968  */
43969
43970 Roo.bootstrap.BezierSignature = function(config){
43971     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43972     this.addEvents({
43973         "resize" : true
43974     });
43975 };
43976
43977 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43978 {
43979      
43980     curve_data: [],
43981     
43982     is_empty: true,
43983     
43984     mouse_btn_down: true,
43985     
43986     /**
43987      * @cfg {int} canvas height
43988      */
43989     canvas_height: '200px',
43990     
43991     /**
43992      * @cfg {float|function} Radius of a single dot.
43993      */ 
43994     dot_size: false,
43995     
43996     /**
43997      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43998      */
43999     min_width: 0.5,
44000     
44001     /**
44002      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44003      */
44004     max_width: 2.5,
44005     
44006     /**
44007      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44008      */
44009     throttle: 16,
44010     
44011     /**
44012      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44013      */
44014     min_distance: 5,
44015     
44016     /**
44017      * @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.
44018      */
44019     bg_color: 'rgba(0, 0, 0, 0)',
44020     
44021     /**
44022      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44023      */
44024     dot_color: 'black',
44025     
44026     /**
44027      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44028      */ 
44029     velocity_filter_weight: 0.7,
44030     
44031     /**
44032      * @cfg {function} Callback when stroke begin. 
44033      */
44034     onBegin: false,
44035     
44036     /**
44037      * @cfg {function} Callback when stroke end.
44038      */
44039     onEnd: false,
44040     
44041     getAutoCreate : function()
44042     {
44043         var cls = 'roo-signature column';
44044         
44045         if(this.cls){
44046             cls += ' ' + this.cls;
44047         }
44048         
44049         var col_sizes = [
44050             'lg',
44051             'md',
44052             'sm',
44053             'xs'
44054         ];
44055         
44056         for(var i = 0; i < col_sizes.length; i++) {
44057             if(this[col_sizes[i]]) {
44058                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44059             }
44060         }
44061         
44062         var cfg = {
44063             tag: 'div',
44064             cls: cls,
44065             cn: [
44066                 {
44067                     tag: 'div',
44068                     cls: 'roo-signature-body',
44069                     cn: [
44070                         {
44071                             tag: 'canvas',
44072                             cls: 'roo-signature-body-canvas',
44073                             height: this.canvas_height,
44074                             width: this.canvas_width
44075                         }
44076                     ]
44077                 },
44078                 {
44079                     tag: 'input',
44080                     type: 'file',
44081                     style: 'display: none'
44082                 }
44083             ]
44084         };
44085         
44086         return cfg;
44087     },
44088     
44089     initEvents: function() 
44090     {
44091         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44092         
44093         var canvas = this.canvasEl();
44094         
44095         // mouse && touch event swapping...
44096         canvas.dom.style.touchAction = 'none';
44097         canvas.dom.style.msTouchAction = 'none';
44098         
44099         this.mouse_btn_down = false;
44100         canvas.on('mousedown', this._handleMouseDown, this);
44101         canvas.on('mousemove', this._handleMouseMove, this);
44102         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44103         
44104         if (window.PointerEvent) {
44105             canvas.on('pointerdown', this._handleMouseDown, this);
44106             canvas.on('pointermove', this._handleMouseMove, this);
44107             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44108         }
44109         
44110         if ('ontouchstart' in window) {
44111             canvas.on('touchstart', this._handleTouchStart, this);
44112             canvas.on('touchmove', this._handleTouchMove, this);
44113             canvas.on('touchend', this._handleTouchEnd, this);
44114         }
44115         
44116         Roo.EventManager.onWindowResize(this.resize, this, true);
44117         
44118         // file input event
44119         this.fileEl().on('change', this.uploadImage, this);
44120         
44121         this.clear();
44122         
44123         this.resize();
44124     },
44125     
44126     resize: function(){
44127         
44128         var canvas = this.canvasEl().dom;
44129         var ctx = this.canvasElCtx();
44130         var img_data = false;
44131         
44132         if(canvas.width > 0) {
44133             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44134         }
44135         // setting canvas width will clean img data
44136         canvas.width = 0;
44137         
44138         var style = window.getComputedStyle ? 
44139             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44140             
44141         var padding_left = parseInt(style.paddingLeft) || 0;
44142         var padding_right = parseInt(style.paddingRight) || 0;
44143         
44144         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44145         
44146         if(img_data) {
44147             ctx.putImageData(img_data, 0, 0);
44148         }
44149     },
44150     
44151     _handleMouseDown: function(e)
44152     {
44153         if (e.browserEvent.which === 1) {
44154             this.mouse_btn_down = true;
44155             this.strokeBegin(e);
44156         }
44157     },
44158     
44159     _handleMouseMove: function (e)
44160     {
44161         if (this.mouse_btn_down) {
44162             this.strokeMoveUpdate(e);
44163         }
44164     },
44165     
44166     _handleMouseUp: function (e)
44167     {
44168         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44169             this.mouse_btn_down = false;
44170             this.strokeEnd(e);
44171         }
44172     },
44173     
44174     _handleTouchStart: function (e) {
44175         
44176         e.preventDefault();
44177         if (e.browserEvent.targetTouches.length === 1) {
44178             // var touch = e.browserEvent.changedTouches[0];
44179             // this.strokeBegin(touch);
44180             
44181              this.strokeBegin(e); // assume e catching the correct xy...
44182         }
44183     },
44184     
44185     _handleTouchMove: function (e) {
44186         e.preventDefault();
44187         // var touch = event.targetTouches[0];
44188         // _this._strokeMoveUpdate(touch);
44189         this.strokeMoveUpdate(e);
44190     },
44191     
44192     _handleTouchEnd: function (e) {
44193         var wasCanvasTouched = e.target === this.canvasEl().dom;
44194         if (wasCanvasTouched) {
44195             e.preventDefault();
44196             // var touch = event.changedTouches[0];
44197             // _this._strokeEnd(touch);
44198             this.strokeEnd(e);
44199         }
44200     },
44201     
44202     reset: function () {
44203         this._lastPoints = [];
44204         this._lastVelocity = 0;
44205         this._lastWidth = (this.min_width + this.max_width) / 2;
44206         this.canvasElCtx().fillStyle = this.dot_color;
44207     },
44208     
44209     strokeMoveUpdate: function(e)
44210     {
44211         this.strokeUpdate(e);
44212         
44213         if (this.throttle) {
44214             this.throttleStroke(this.strokeUpdate, this.throttle);
44215         }
44216         else {
44217             this.strokeUpdate(e);
44218         }
44219     },
44220     
44221     strokeBegin: function(e)
44222     {
44223         var newPointGroup = {
44224             color: this.dot_color,
44225             points: []
44226         };
44227         
44228         if (typeof this.onBegin === 'function') {
44229             this.onBegin(e);
44230         }
44231         
44232         this.curve_data.push(newPointGroup);
44233         this.reset();
44234         this.strokeUpdate(e);
44235     },
44236     
44237     strokeUpdate: function(e)
44238     {
44239         var rect = this.canvasEl().dom.getBoundingClientRect();
44240         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44241         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44242         var lastPoints = lastPointGroup.points;
44243         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44244         var isLastPointTooClose = lastPoint
44245             ? point.distanceTo(lastPoint) <= this.min_distance
44246             : false;
44247         var color = lastPointGroup.color;
44248         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44249             var curve = this.addPoint(point);
44250             if (!lastPoint) {
44251                 this.drawDot({color: color, point: point});
44252             }
44253             else if (curve) {
44254                 this.drawCurve({color: color, curve: curve});
44255             }
44256             lastPoints.push({
44257                 time: point.time,
44258                 x: point.x,
44259                 y: point.y
44260             });
44261         }
44262     },
44263     
44264     strokeEnd: function(e)
44265     {
44266         this.strokeUpdate(e);
44267         if (typeof this.onEnd === 'function') {
44268             this.onEnd(e);
44269         }
44270     },
44271     
44272     addPoint:  function (point) {
44273         var _lastPoints = this._lastPoints;
44274         _lastPoints.push(point);
44275         if (_lastPoints.length > 2) {
44276             if (_lastPoints.length === 3) {
44277                 _lastPoints.unshift(_lastPoints[0]);
44278             }
44279             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44280             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44281             _lastPoints.shift();
44282             return curve;
44283         }
44284         return null;
44285     },
44286     
44287     calculateCurveWidths: function (startPoint, endPoint) {
44288         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44289             (1 - this.velocity_filter_weight) * this._lastVelocity;
44290
44291         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44292         var widths = {
44293             end: newWidth,
44294             start: this._lastWidth
44295         };
44296         
44297         this._lastVelocity = velocity;
44298         this._lastWidth = newWidth;
44299         return widths;
44300     },
44301     
44302     drawDot: function (_a) {
44303         var color = _a.color, point = _a.point;
44304         var ctx = this.canvasElCtx();
44305         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44306         ctx.beginPath();
44307         this.drawCurveSegment(point.x, point.y, width);
44308         ctx.closePath();
44309         ctx.fillStyle = color;
44310         ctx.fill();
44311     },
44312     
44313     drawCurve: function (_a) {
44314         var color = _a.color, curve = _a.curve;
44315         var ctx = this.canvasElCtx();
44316         var widthDelta = curve.endWidth - curve.startWidth;
44317         var drawSteps = Math.floor(curve.length()) * 2;
44318         ctx.beginPath();
44319         ctx.fillStyle = color;
44320         for (var i = 0; i < drawSteps; i += 1) {
44321         var t = i / drawSteps;
44322         var tt = t * t;
44323         var ttt = tt * t;
44324         var u = 1 - t;
44325         var uu = u * u;
44326         var uuu = uu * u;
44327         var x = uuu * curve.startPoint.x;
44328         x += 3 * uu * t * curve.control1.x;
44329         x += 3 * u * tt * curve.control2.x;
44330         x += ttt * curve.endPoint.x;
44331         var y = uuu * curve.startPoint.y;
44332         y += 3 * uu * t * curve.control1.y;
44333         y += 3 * u * tt * curve.control2.y;
44334         y += ttt * curve.endPoint.y;
44335         var width = curve.startWidth + ttt * widthDelta;
44336         this.drawCurveSegment(x, y, width);
44337         }
44338         ctx.closePath();
44339         ctx.fill();
44340     },
44341     
44342     drawCurveSegment: function (x, y, width) {
44343         var ctx = this.canvasElCtx();
44344         ctx.moveTo(x, y);
44345         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44346         this.is_empty = false;
44347     },
44348     
44349     clear: function()
44350     {
44351         var ctx = this.canvasElCtx();
44352         var canvas = this.canvasEl().dom;
44353         ctx.fillStyle = this.bg_color;
44354         ctx.clearRect(0, 0, canvas.width, canvas.height);
44355         ctx.fillRect(0, 0, canvas.width, canvas.height);
44356         this.curve_data = [];
44357         this.reset();
44358         this.is_empty = true;
44359     },
44360     
44361     fileEl: function()
44362     {
44363         return  this.el.select('input',true).first();
44364     },
44365     
44366     canvasEl: function()
44367     {
44368         return this.el.select('canvas',true).first();
44369     },
44370     
44371     canvasElCtx: function()
44372     {
44373         return this.el.select('canvas',true).first().dom.getContext('2d');
44374     },
44375     
44376     getImage: function(type)
44377     {
44378         if(this.is_empty) {
44379             return false;
44380         }
44381         
44382         // encryption ?
44383         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44384     },
44385     
44386     drawFromImage: function(img_src)
44387     {
44388         var img = new Image();
44389         
44390         img.onload = function(){
44391             this.canvasElCtx().drawImage(img, 0, 0);
44392         }.bind(this);
44393         
44394         img.src = img_src;
44395         
44396         this.is_empty = false;
44397     },
44398     
44399     selectImage: function()
44400     {
44401         this.fileEl().dom.click();
44402     },
44403     
44404     uploadImage: function(e)
44405     {
44406         var reader = new FileReader();
44407         
44408         reader.onload = function(e){
44409             var img = new Image();
44410             img.onload = function(){
44411                 this.reset();
44412                 this.canvasElCtx().drawImage(img, 0, 0);
44413             }.bind(this);
44414             img.src = e.target.result;
44415         }.bind(this);
44416         
44417         reader.readAsDataURL(e.target.files[0]);
44418     },
44419     
44420     // Bezier Point Constructor
44421     Point: (function () {
44422         function Point(x, y, time) {
44423             this.x = x;
44424             this.y = y;
44425             this.time = time || Date.now();
44426         }
44427         Point.prototype.distanceTo = function (start) {
44428             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44429         };
44430         Point.prototype.equals = function (other) {
44431             return this.x === other.x && this.y === other.y && this.time === other.time;
44432         };
44433         Point.prototype.velocityFrom = function (start) {
44434             return this.time !== start.time
44435             ? this.distanceTo(start) / (this.time - start.time)
44436             : 0;
44437         };
44438         return Point;
44439     }()),
44440     
44441     
44442     // Bezier Constructor
44443     Bezier: (function () {
44444         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44445             this.startPoint = startPoint;
44446             this.control2 = control2;
44447             this.control1 = control1;
44448             this.endPoint = endPoint;
44449             this.startWidth = startWidth;
44450             this.endWidth = endWidth;
44451         }
44452         Bezier.fromPoints = function (points, widths, scope) {
44453             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44454             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44455             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44456         };
44457         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44458             var dx1 = s1.x - s2.x;
44459             var dy1 = s1.y - s2.y;
44460             var dx2 = s2.x - s3.x;
44461             var dy2 = s2.y - s3.y;
44462             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44463             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44464             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44465             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44466             var dxm = m1.x - m2.x;
44467             var dym = m1.y - m2.y;
44468             var k = l2 / (l1 + l2);
44469             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44470             var tx = s2.x - cm.x;
44471             var ty = s2.y - cm.y;
44472             return {
44473                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44474                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44475             };
44476         };
44477         Bezier.prototype.length = function () {
44478             var steps = 10;
44479             var length = 0;
44480             var px;
44481             var py;
44482             for (var i = 0; i <= steps; i += 1) {
44483                 var t = i / steps;
44484                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44485                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44486                 if (i > 0) {
44487                     var xdiff = cx - px;
44488                     var ydiff = cy - py;
44489                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44490                 }
44491                 px = cx;
44492                 py = cy;
44493             }
44494             return length;
44495         };
44496         Bezier.prototype.point = function (t, start, c1, c2, end) {
44497             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44498             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44499             + (3.0 * c2 * (1.0 - t) * t * t)
44500             + (end * t * t * t);
44501         };
44502         return Bezier;
44503     }()),
44504     
44505     throttleStroke: function(fn, wait) {
44506       if (wait === void 0) { wait = 250; }
44507       var previous = 0;
44508       var timeout = null;
44509       var result;
44510       var storedContext;
44511       var storedArgs;
44512       var later = function () {
44513           previous = Date.now();
44514           timeout = null;
44515           result = fn.apply(storedContext, storedArgs);
44516           if (!timeout) {
44517               storedContext = null;
44518               storedArgs = [];
44519           }
44520       };
44521       return function wrapper() {
44522           var args = [];
44523           for (var _i = 0; _i < arguments.length; _i++) {
44524               args[_i] = arguments[_i];
44525           }
44526           var now = Date.now();
44527           var remaining = wait - (now - previous);
44528           storedContext = this;
44529           storedArgs = args;
44530           if (remaining <= 0 || remaining > wait) {
44531               if (timeout) {
44532                   clearTimeout(timeout);
44533                   timeout = null;
44534               }
44535               previous = now;
44536               result = fn.apply(storedContext, storedArgs);
44537               if (!timeout) {
44538                   storedContext = null;
44539                   storedArgs = [];
44540               }
44541           }
44542           else if (!timeout) {
44543               timeout = window.setTimeout(later, remaining);
44544           }
44545           return result;
44546       };
44547   }
44548   
44549 });
44550
44551  
44552
44553